POSIX 쉘 스크립트에서 변수가 정수인지 확인하는 방법(주변 공백 문제 방지)

POSIX 쉘 스크립트에서 변수가 정수인지 확인하는 방법(주변 공백 문제 방지)

is_integer ()오랫동안 내 POSIX 함수는 다음과 같았습니다.

#!/bin/sh
is_integer ()
{
    [ "$1" -eq "$1" ] 2> /dev/null
}

그런데 오늘 나는 발견했다.부서진. 숫자 주위에 공백이 있으면 놀랍게도 계산이 잘 되는데, true이 문제를 어떻게 해결해야 할지 모르겠습니다.


올바른(예상되는) 동작의 예:

is_integer 123으로 평가됩니다 true.

잘못된(예기치 않은) 동작의 예:

is_integer ' 123'로도 평가됨truefalse이지만 분명히 선행 공백이 포함되어 있으므로 이 경우 함수는 로 평가될 것으로 예상됩니다.


POSIX 호환 제안만 제공하세요. 감사해요.

답변1

#!/bin/sh
is_integer ()
{
    case "${1#[+-]}" in
        (*[!0123456789]*) return 1 ;;
        ('')              return 1 ;;
        (*)               return 0 ;;
    esac
}

POSIX 내장 함수만 사용하세요. +1정수여야 하는지 사양에서 명확하지 않으며 , 그렇지 않은 경우 줄 +에서 제거됩니다 case.

작동 방식은 다음과 같습니다. ${1#[+-]}선택적 선행 기호를 제거합니다 . 숫자가 아닌 것을 포함하는 것을 제외하면 아무것도 남기지 않는 것과 마찬가지로 정수가 아닙니다. 정수가 아닌 경우 정수입니다.

편집: 문자 클래스를 무효화하기 위해 ^를 !로 변경했습니다. @LinuxSecurityFreak에게 감사드립니다.

답변2

(외부 명령으로 인해) 가장 효율적이지는 않지만 매우 간단합니다.

is_integer () {
  expr "X$1" : "X-\{0,1\}[0-9][0-9]*$" > /dev/null
}

적어도 테스트 중인 구현에서는 초기 인수가 일치 연산의 일부로 처리되지 않지만 해당 인수 가 유효한 일치 연산으로 구문 분석되도록 -보장하는 유효하지 않은 산술 표현식의 일부로 처리되는 것 같습니다 .Xexpr

답변3

보다 완벽한 솔루션은 다음과 같습니다.

is_integer() (
    export LC_ALL=C
    local n=${1#[-+]}
    case "$n" in
        0[0-7]*) case "$n" in 0*[!0-7]*)                 return 1;; esac;;
        0[xX]*)  case "$n" in 0[xX]|0[xX]*[!0-9a-fA-F]*) return 1;; esac;;
        *)       case "$n" in ''|*[!0-9]*)               return 1;; esac;;
    esac
)

이렇게 하면 선행 기호가 모두 제거된 다음 0, 또는 접두사가 있는지 여부에 따라 0x문자열을 구문 분석합니다 0X. 따라서 십진수로 사용되는 값에는 앞에 0이 오지 않도록 주의해야 합니다.

$ echo $((01))
1
$ echo $((08))
-ash: arithmetic syntax error

관련 정보