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。したがって、10進数として使用される値には前にゼロが来ないように注意する必要があります。

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

関連情報