문자열이 유효한 정수인지 테스트


117

나는 셸 스크립트에서 사용자 입력을 구문 분석하는 것입니다. 사용자가 유효한 정수를 제공 한 경우 스크립트는 한 가지 작업을 수행하고 유효하지 않은 경우 다른 작업을 수행합니다. 문제는이 작업을 수행하는 쉽고 (합리적으로 우아한) 방법을 찾지 못했다는 것입니다. 문자별로 구분할 필요가 없습니다.

이게 쉬울 거라는 건 알지만 방법은 모르겠어요. 12 개 언어로 할 수 있지만 BASH는 아닙니다!

내 연구에서 나는 이것을 발견했습니다.

문자열이 10 진법의 유효한 실수로 구성되어 있는지 테스트하기위한 정규식

그리고 거기에 정규식에 대해 이야기하는 대답이 있지만 내가 아는 한 그것은 C에서 사용할 수있는 함수입니다. 그래도 좋은 대답처럼 보였기 때문에 grep으로 시도했지만 grep은 어떻게 해야할지 몰랐습니다. 나는 내 상자에서 PERL 정규 표현식으로 취급한다는 것을 의미하는 -P를 시도했습니다. 대시 E (-E)도 작동하지 않았습니다. 그리고 -F도 마찬가지입니다.

명확하게 말하면, 나는 이와 같은 것을 시도하고, 출력을 찾고 있습니다. 거기에서 내가 얻는 모든 것을 활용하기 위해 스크립트를 해킹 할 것입니다. (IOW, 유효한 라인이 반복되는 동안 부적합한 입력은 아무것도 반환하지 않을 것으로 예상했습니다.)

snafu=$(echo "$2" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
   echo "Not an integer - nothing back from the grep"
else
   echo "Integer."
fi

누군가 이것이 가장 쉬운 방법을 설명해 주시겠습니까?

솔직히 이것은 TEST의 단점이라고 생각합니다. 다음과 같은 플래그가 있어야합니다.

if [ -I "string" ] ;
then
   echo "String is a valid integer."
else
   echo "String is not a valid integer."
fi

4
참고 : [이전 버전과 호환됩니다 test. [[더 많은 작업과 다른 인용 규칙을 가진 Bash의 새로운 것입니다. 이미 Bash를 고수하기로 결정했다면 [[(정말 훨씬 더 좋습니다 .) 다른 셸로의 이식성이 필요한 경우 [[완전히 피하십시오 .
ephemient

답변:


183
[[ $var =~ ^-?[0-9]+$ ]]
  • ^입력 패턴의 시작을 나타낸다
  • -리터럴 "-"
  • ?수단 "0 또는 1 항 ( -)"
  • +수단 "앞의 1 이상 ( [0-9])"
  • $입력 패턴의 끝을 나타낸다

따라서 정규식은 선택 사항 -(음수의 경우)과 그 뒤에 하나 이상의 십진수를 일치 시킵니다.

참고 문헌 :


3
감사합니다 Ignacio, 잠시 후 시도해 보겠습니다. 제가 조금 배울 수 있도록 설명해 주시겠습니까? "문자열 (^)의 시작 부분에서 빼기 기호 (-)는 선택 사항 (?)이고 그 뒤에 0과 9 사이의 문자가 포함됩니다."라는 메시지가 표시됩니다. 그러면 + $은 무슨 뜻인가요? 감사.
Richard T

10
+수단 "1 항 이상"과는 $입력 패턴의 끝을 나타낸다. 따라서 정규식은 선택적 -뒤에 하나 이상의 십진수 와 일치합니다 .
Ignacio Vazquez-Abrams

투덜는 다시 : ABS 링크
찰스 더피

접선이지만 문자 범위를 지정할 때 이상한 결과를 얻을 수 있습니다. 예를 들어, [A-z]만주지 못할 것이다 A-Z및에게 a-z뿐만 아니라 \ , [, ], ^, _,와 `.
Doktor J

또한, 캐릭터의 조합에 따라 ( 이 관련 질문 / 답변을 참조 같은 것을)을 d[g-i]{2}끝낼 수 있었다뿐만 아니라 일치 dig뿐만 아니라 dish합니다 (그 대답에 의해 제안 된 조합의 sh소리를 나타내는 두 글자가 후에 대조, 단일 문자로 간주됩니다 h).
Doktor J

61

와우 ... 여기에 좋은 해결책이 너무 많습니다 !! 위의 모든 솔루션 중에서 -eq하나의 라이너 를 사용하는 것이 가장 멋지다는 @nortally에 동의합니다 .

GNU bash, 버전 4.1.5(Debian)을 실행 중 입니다. ksh (SunSO 5.10)에서도 이것을 확인했습니다.

$1정수인지 아닌지 확인하는 내 버전은 다음과 같습니다 .

if [ "$1" -eq "$1" ] 2>/dev/null
then
    echo "$1 is an integer !!"
else
    echo "ERROR: first parameter must be an integer."
    echo $USAGE
    exit 1
fi

이 접근법은 또한 다른 솔루션 중 일부가 잘못된 음수 결과를 갖는 음수를 설명하며 분명히 정수인 "+"(예 : +30) 접두사를 허용합니다.

결과 :

$ int_check.sh 123
123 is an integer !!

$ int_check.sh 123+
ERROR: first parameter must be an integer.

$ int_check.sh -123
-123 is an integer !!

$ int_check.sh +30
+30 is an integer !!

$ int_check.sh -123c
ERROR: first parameter must be an integer.

$ int_check.sh 123c
ERROR: first parameter must be an integer.

$ int_check.sh c123
ERROR: first parameter must be an integer.

Ignacio Vazquez-Abrams가 제공 한 솔루션은 설명 된 후에도 매우 깔끔했습니다 (정규식을 좋아한다면). 그러나 +접두사 가있는 양수를 처리하지 않지만 다음과 같이 쉽게 수정할 수 있습니다.

[[ $var =~ ^[-+]?[0-9]+$ ]]

좋은! 그래도 이것 과 매우 비슷합니다 .
devnull

예. 비슷합니다. 그러나 나는 "if"서술문에 대한 하나의 라이너 솔루션을 찾고있었습니다. 나는 이것을 위해 함수를 호출 할 필요가 없다고 생각했습니다. 또한 함수에서 stderr가 stdout으로 리디렉션되는 것을 볼 수 있습니다. 내가 시도했을 때, stderr 메시지 "정수 표현식이 예상 됨"이 표시되었는데 이는 나에게 바람직하지 않았습니다.
Peter Ho

감사합니다! 나는 이것을 쉽고 우아하다고 부를 것입니다.
Ezra Nugroho

2
솔루션과 정규식 솔루션 사이에는 눈에 띄는 차이가 있습니다. 정수의 크기는 bash 제한으로 확인됩니다 (내 컴퓨터에서는 64 비트). 이 제한은 정규 표현식 솔루션에 도달하지 않습니다. 따라서 솔루션은 64 비트 컴퓨터에서 9223372036854775807보다 엄격하게 큰 수에서 실패합니다.
vaab

2
최근에 발견했듯이 몇 가지주의 사항이 있습니다.
Kyle Strand

28

여기 파티에 후발. 가장 간단하고 빠르며 휴대 성이 뛰어난 솔루션을 언급 한 답변이 없다는 사실에 매우 놀랐습니다. case문.

case ${variable#[-+]} in
  *[!0-9]* | '') echo Not a number ;;
  * ) echo Valid number ;;
esac

비교하기 전에 모든 기호를 잘라내는 것은 약간의 해킹처럼 느껴지지만 case 문에 대한 표현이 훨씬 간단 해집니다.


4
나는 속임수 때문에이 질문에 다시 올 때마다 이것을 한 번 찬성 할 수 있기를 바랍니다. 단순하지만 POSIX를 준수하는 솔루션이 바닥에 묻혀 있다는 사실은 내 기어를 갈아 입 힙니다.
Adrian Frühwirth 2014

3
빈 문자열을 처리해야 할 수도 있습니다.''|*[!0-9]*)
Niklas Peter

2
BTW :이 구문은 다음과 같습니다. tldp.org/LDP/abs/html/string-manipulation.html
Niklas Peter

나는 ABS를 특별히 용납하지 않습니다. 이것은 Bash 매뉴얼에도 분명히 문서화되어 있습니다. 어쨌든, 당신이 링크 한 섹션은이 특정 구조를 설명하지 않고 오히려 @Nortally의 대답을 설명합니다.
tripleee

@tripleee 링크 된 문서는 케이스 라인에 사용 된 변수에서 문자열 접두사를 제거하기위한 구성을 설명합니다. 그냥 페이지의 하단에 있지만 앵커 내가 직접 링크를 연결하지 수 있도록, "제거를 하위 문자열"절을 참조 없다
니클라스 피터

10

-eq테스트를 사용하는 솔루션이 마음에 듭니다. 기본적으로 한 줄짜리이기 때문입니다.

내 해결책은 매개 변수 확장을 사용하여 모든 숫자를 버리고 남은 것이 있는지 확인하는 것이 었습니다. (저는 아직 3.0을 사용하고 있으며 사용하지 않았 [[거나 expr이전에 사용했지만 만나서 반갑습니다.)

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi

4
이것은 [ -z "${INPUT_STRING//[0-9]}" ]정말 좋은 솔루션을 사용하여 더 향상시킬 수 있습니다 !
ShellFish

음수 기호는 어떻습니까?
scottysseus

-eq솔루션은 몇 가지 문제가있다; 여기를 참조하십시오 : stackoverflow.com/a/808740/1858225
Kyle Strand

빈 INPUT_STRING은 숫자로 간주되므로 제 경우에 실패합니다
Manwe

9

Bash 3.1 이전 버전으로의 이식성을 위해 ( =~테스트가 도입 되었을 때 ) expr.

if expr "$string" : '-\?[0-9]\+$' >/dev/null
then
  echo "String is a valid integer."
else
  echo "String is not a valid integer."
fi

expr STRING : REGEXSTRING의 시작 부분에 고정 된 REGEX를 검색하여 첫 번째 그룹 (또는 일치하지 않는 경우 일치 길이)을 반영하고 성공 / 실패를 반환합니다. 이것은 오래된 정규식 구문이므로 초과 \. -\?"아마도 -"를 [0-9]\+의미하고 "하나 이상의 숫자"를 $의미하며 "문자열의 끝"을 의미합니다.

Bash는 또한 확장 된 glob을 지원하지만 어떤 버전에서 이후 버전인지 기억하지 못합니다.

shopt -s extglob
case "$string" of
    @(-|)[0-9]*([0-9]))
        echo "String is a valid integer." ;;
    *)
        echo "String is not a valid integer." ;;
esac

# equivalently, [[ $string = @(-|)[0-9]*([0-9])) ]]

@(-|)" -또는 아무것도"를 [0-9]의미하고 "숫자"를 *([0-9])의미하며 "0 개 이상의 숫자"를 의미합니다.


ephemient, 많은 의무 감사합니다. 나는 전에 = ~ 구문을 본 적이 없었습니다. 그리고 그것이 무엇을 의미하는지 여전히 모릅니다. ... 나는 BASH의 프로그램에 흥분 적이 없습니다하지만 입니다 몇 번 필요!
Richard T

에서은 awk, ~은 "정규 표현식 일치"연산자이었다. Perl (C에서 복사)에서는 ~이미 "비트 보완"에 사용되었으므로 =~. 나중에이 표기법은 여러 다른 언어로 복사되었습니다. (Perl 5.10과 Perl 6은 ~~더 좋아 하지만 여기에는 영향이 없습니다.) 나는 당신이 그것을 일종의 근사 평등으로 볼 수 있다고 생각합니다 ...
ephemient

훌륭한 게시물 및 편집! 그것이 의미하는 바를 설명해 주셔서 정말 감사합니다. 나는 당신과 Ignacio의 게시물을 모두 정답으로 표시하고 싶습니다. -찡그린 얼굴-너희 둘 다 대단해. 하지만 당신이 그의 명성을 두 배로했기 때문에 나는 그것을 Ignacio에게 줄 것입니다-당신이 이해하기를 바랍니다! -smile-
Richard T

4

여기에 또 다른 방법이 있습니다 (테스트 내장 명령과 반환 코드 만 사용).

function is_int() { return $(test "$@" -eq "$@" > /dev/null 2>&1); } 

input="-123"

if $(is_int "${input}");
then
   echo "Input: ${input}"
   echo "Integer: $[${input}]"
else
   echo "Not an integer: ${input}"
fi

1
$()와 함께 사용할 필요는 없습니다 if. 이것은 작동합니다 : if is_int "$input". 또한 $[]양식은 더 이상 사용되지 않습니다. $(())대신 사용하십시오 . 둘 중 하나에서 달러 기호는 생략 할 수 있습니다 echo "Integer: $((input))". 스크립트의 어느 곳에서도 중괄호가 필요하지 않습니다.
추후 공지가있을 때까지 일시 중지되었습니다.

나는 이것이 또한 Bash의 기본 표기법의 숫자를 유효한 정수로 처리 할 것으로 예상했을 것입니다 (물론 어떤 정의에 의해 그것들은 그렇습니다; 그러나 그것은 당신의 것과 일치 test하지 않을 수도 있습니다). 그러나 이것을 지원하지 않는 것 같습니다. [[그래도 그렇습니다. [[ 16#aa -eq 16#aa ]] && echo integer"정수"를 인쇄합니다.
tripleee

참고 [[이 방법에 대한 오탐 (false positive)을 반환 예를 들어 [[ f -eq f ]]성공합니다. 따라서 test또는을 사용해야합니다 [.
스핀 업

3

숫자가 아닌 것을 제거하고 비교할 수 있습니다. 다음은 데모 스크립트입니다.

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09"
do
    match=${num//[^[:digit:]]}    # strip non-digits
    match=${match#0*}             # strip leading zeros
    echo -en "$num\t$match\t"
    case $num in
        $match|-$match)    echo "Integer";;
                     *)    echo "Not integer";;
    esac
done

테스트 출력은 다음과 같습니다.

44 44 정수
-44 44 정수
44-44 정수 아님
4-4 44 정수 아님
a4 4 정수 아님
4a 4 정수 아님
.4 4 정수 아님
4.4 44 정수 아님
-4.4 44 정수 아님
09 9 정수 아님

안녕하세요 Dennis, 위의 match = 오른쪽에있는 구문을 소개해 주셔서 감사합니다. 나는 전에 그 유형 구문을 본 적이 없습니다. 나는 tr의 구문 중 일부를 알고있다 (내가 충분히 숙달하지는 않았지만 가끔씩 엉망이되는 유틸리티). 그러한 구문에 대해 어디서 읽을 수 있습니까? (즉,이 유형의 이름은 무엇입니까?) 감사합니다.
Richard T

"매개 변수 확장"섹션의 Bash 매뉴얼 페이지에서 [^ [: digit :]]`에 대한 정보 ${var//string}${var#string}"패턴 일치"섹션을 참조하십시오 (에서도 다룹니다 man 7 regex).
추후 공지가있을 때까지 일시 중지되었습니다.

1
match=${match#0*}선행 0을 제거 하지 않고 최대 1 개의 0을 제거합니다. 이것 만 사용하여 달성 될 수 팽창하여 extglob비아 match=${match##+(0)}.
Adrian Frühwirth 2014

9 또는 09는 정수가 아닙니까?
Mike Q

@MikeQ : 09정수에 선행 0이없는 것으로 간주하면 정수가 아닙니다. 테스트는 입력 ( 09)이 삭제 된 버전 ( 9-정수)과 같고 그렇지 않은지 여부입니다.
추후 공지가있을 때까지 일시 중지되었습니다.

2

저에게 가장 간단한 해결책은 (())표현식 내부에서 변수를 사용하는 것입니다.

if ((VAR > 0))
then
  echo "$VAR is a positive integer."
fi

물론이 솔루션은 0 값이 애플리케이션에 적합하지 않은 경우에만 유효합니다. 그것은 제 경우에 사실이었고 이것은 다른 솔루션보다 훨씬 간단합니다.

주석에서 지적했듯이 이것은 코드 실행 공격에 노출 될 수 있습니다. (( ))연산자 는 bash (1) 매뉴얼 페이지VARArithmetic Evaluation섹션에 명시된대로를 평가 합니다 . 따라서의 내용의 출처 VAR가 불확실 할 때이 기술 을 사용해서는 안됩니다 (물론 다른 형태의 변수 확장을 사용해서는 안됩니다).


더 간단하게 할 수 있습니다if (( var )); then echo "$var is an int."; fi
Aaron R.

2
그러나 그것은 OP가 찾고 있던 것이 아니라 음의 정수 @aaronr에 대해서도 true를 반환합니다.
Trebor Rude 2014

2
이것은 위험합니다. 참조 : n = 1; var = "n"; if ((var)); then echo "$ var는 정수입니다."; fi
jarno

2
이것은 매우 나쁜 생각이며 임의의 코드 실행의 대상이됩니다 VAR='a[$(ls)]'; if ((VAR > 0)); then echo "$VAR is a positive integer"; fi. 직접 시도해보십시오 .. 이 시점에서 내가 대신 악의적 인 명령을 입력하지 않았다는 것이 기쁩니다 ls. OP는 사용자 입력을 언급하기 때문에 프로덕션 코드에서 사용자 입력과 함께 이것을 사용하지 않기를 바랍니다!
gniourf_gniourf

문자열에 다음과 같은 숫자가 포함되어 있으면 작동하지 않습니다.agent007
brablc

1

또는 sed :

   test -z $(echo "2000" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # integer

   test -z $(echo "ab12" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # no integer

Bash 및 일부 다른 "Bourne plus"셸에서 명령 대체 및 외부 명령을 피할 수 있습니다. test -z "${string//[0-9]/}" && echo "integer" || echo "no integer"기본적으로 Dennis Williamson의 답변과
tripleee

감사! 실제로 여기에서 작동하는 유일한 대답!
사용자

무음 대안 :if [[ -n "$(printf "%s" "${2}" | sed s/[0-9]//g)" ]]; then
사용자

0

Ignacio Vazquez-Abrams의 답변에 추가합니다. 이렇게하면 + 부호가 정수 앞에 올 수 있으며 소수점으로 모든 0을 허용합니다. 예를 들어 +45.00000000을 정수로 간주 할 수 있습니다.
그러나 $ 1은 소수점을 포함하도록 형식을 지정해야합니다. 45는 여기서 정수로 간주되지 않지만 45.0은 정수로 간주됩니다.

if [[ $1 =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
elif [[ $1 =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi

^[-+]?[0-9]... 대신 양수와 음수에 대해 두 가지 다른 정규식을 사용하는 이유가 있습니까?
tripleee

0

웃음을 위해 나는 대략 빠르게 이것을 수행하기 위해 일련의 함수 (is_string, is_int, is_float, is alpha string, 또는 기타)를 알아 냈지만이를 수행하는 더 효율적인 (더 적은 코드) 방법이 있습니다.

#!/bin/bash

function strindex() {
    x="${1%%$2*}"
    if [[ "$x" = "$1" ]] ;then
        true
    else
        if [ "${#x}" -gt 0 ] ;then
            false
        else
            true
        fi
    fi
}

function is_int() {
    if is_empty "${1}" ;then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^0-9]*//g')
    if [[ $tmp == "${1}" ]] || [[ "-${tmp}" == "${1}" ]] ; then
        #echo "INT (${1}) tmp=$tmp"
        true
    else
        #echo "NOT INT (${1}) tmp=$tmp"
        false
    fi
}

function is_float() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if ! strindex "${1}" "-" ; then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^a-z. ]*//g')
    if [[ $tmp =~ "." ]] ; then
        #echo "FLOAT  (${1}) tmp=$tmp"
        true
    else
        #echo "NOT FLOAT  (${1}) tmp=$tmp"
        false
    fi
}

function is_strict_string() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if [[ "${1}" =~ ^[A-Za-z]+$ ]]; then
        #echo "STRICT STRING (${1})"
        true
    else
        #echo "NOT STRICT STRING (${1})"
        false
    fi
}

function is_string() {
    if is_empty "${1}" || is_int "${1}" || is_float "${1}" || is_strict_string "${1}" ;then
        false
        return
    fi
    if [ ! -z "${1}" ] ;then
        true
        return
    fi
    false
}
function is_empty() {
    if [ -z "${1// }" ] ;then
        true
    else
        false
    fi
}

여기 몇 가지 테스트를 통해 -44는 int이지만 44-는 그렇지 않다고 정의했습니다. :

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09" "hello" "h3llo!" "!!" " " "" ; do
    if is_int "$num" ;then
        echo "INT = $num"

    elif is_float "$num" ;then
        echo "FLOAT = $num"

    elif is_string "$num" ; then
        echo "STRING = $num"

    elif is_strict_string "$num" ; then
        echo "STRICT STRING = $num"
    else
        echo "OTHER = $num"
    fi
done

산출:

INT = 44
INT = -44
STRING = 44-
STRING = 4-4
STRING = a4
STRING = 4a
FLOAT = .4
FLOAT = 4.4
FLOAT = -4.4
INT = 09
STRICT STRING = hello
STRING = h3llo!
STRING = !!
OTHER =  
OTHER = 

참고 : 선행 0은 8 진수와 같은 숫자를 추가 할 때 다른 것을 추론 할 수 있으므로 '09'를 int로 처리하려는 경우 제거하는 것이 좋습니다 (예 : expr 09 + 0sed로 제거).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.