変数がBashの数値であるかどうかをテストするにはどうすればよいですか?


599

スクリプトに渡される引数が数値であるかどうかを確認する方法がわかりません。

私がしたいのは次のようなものです:

test *isnumber* $1 && VAR=$1 || echo "need a number"

何か助けは?


17
余談test && echo "foo" && exit 0 || echo "bar" && exit 1ですが、使用しているアプローチには意図しない副作用が含まれている可能性があります。エコーが失敗した場合(おそらく出力が閉じたFDに出力exit 0される場合)はスキップされ、コードはを試行しecho "bar"ます。それはあまりにもその時に失敗した場合、&&条件が失敗し、それも実行されませんexit 1!/ ifではなく実際のステートメントを使用すると、予期しない副作用が発生しにくくなります。&&||
Charles Duffy

@CharlesDuffyこれは、ほとんどの人が毛深いバグを追跡する必要がある場合にのみ到達するという本当に賢い考えです...!エコーが失敗を返すとは思いもしませんでした。
カミロマーティン

6
パーティーには少し遅れましたが、チャールズが書いた危険性についても知っています。だからここにあなたのための100%フールプルーフ(そして読みやすい)行があります: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }中かっこは物事がサブシェルで実行されるべきではないことを示します(それは間違い()なく代わりに括弧が使われるその方法です)。警告:最後のセミコロンを見逃さないでください。そうしないとbash、醜い(そして最も無意味な)エラーメッセージが出力される可能性があります...
syntaxerror

5
引用符を削除しない限り、Ubuntuでは機能しません。だからそれだけである必要があります[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
トレヴィーニョ

4
"数値"の意味をより具体的にする必要があります。整数?固定小数点数?科学( "e")表記?必要な範囲(64ビットの符号なし値など)はありますか、または書き込むことができる任意の数を許可しますか?
Toby Speight 2016年

回答:


803

1つのアプローチは、次のように正規表現を使用することです。

re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
   echo "error: Not a number" >&2; exit 1
fi

値が整数でない場合は、正規表現を適切に修正することを検討してください。例えば:

^[0-9]+([.][0-9]+)?$

...または、符号付きの数値を処理するには:

^[+-]?[0-9]+([.][0-9]+)?$

7
このアプローチでは+1ですが、小数には注意してください。たとえば、このテストを「1.0」または「1,0」で行うと、「エラー:数値ではありません」と出力されます。
sourcerebels

15
'' exec>&2;が見つかりました エコー...」かなりばかげています。ただ '' echo ...>&2 ''
lhunath 2009年

5
@Ben本当に複数のマイナス記号を処理しますか?私はそれを作るだろう^-?というよりも、^-*あなたが実際に正しく、複数の反転を処理するための作業をやっている場合を除きます。
Charles Duffy

4
@SandraSchlichting今後のすべての出力をstderrに送信します。エコーが1つしかないここでのポイントではありませんが、エラーメッセージが複数の行にまたがる場合によくある癖です。
Charles Duffy、

29
なぜ正規表現を変数に保存する必要があるのか​​はわかりませんが、互換性のために必要な場合は必要ないと思います。式を直接適用することもできます[[ $yournumber =~ ^[0-9]+$ ]]
konsolebox 2013

284

バシズムなし(System V shでも機能)、

case $string in
    ''|*[!0-9]*) echo bad ;;
    *) echo good ;;
esac

これにより、空の文字列および数字以外の文字列を拒否し、その他すべてを受け入れます。

負の数または浮動小数点数には、追加の作業が必要です。最初の「悪い」パターンで-/ を除外.し、それらの不適切な使用を含む「悪い」パターンをさらに追加する(?*-*/ *.*.*


20
+1-これは、元のBourneシェルに戻る慣用的で移植可能な方法であり、グロブスタイルのワイルドカードのサポートが組み込まれています。別のプログラミング言語から来た場合、それは不気味に見えますが、さまざまな引用の問題の脆弱性や無限の後方/横互換性の問題に対処するよりもはるかにエレガントですif test ...
tripleee

6
最初の行を${string#-}(アンティークのBourneシェルでは機能しませんが、POSIXシェルでは機能します)に変更して、負の整数を受け入れることができます。
Gilles 'SO-悪をやめる

4
また、これはフロートに拡張するのも簡単'.' | *.*.*です。許可されていないパターンに追加し、許可されている文字にドットを追加するだけです。同様に、前にオプションの記号を許可することもできますが、その場合case ${string#[-+]}は単に記号を無視したいと思います。
Tripleee、2014年

符号付き整数の処理については、こちらをご覧ください。stackoverflow.com
a

2
@Dorいずれにしても、caseコマンドは単語の分割やそのパス名の生成を実行しないため、引用符は必要ありません。(ただし、パターン一致文字がリテラルか特殊かを決定するため、パターンの展開では引用が必要になる場合があります。)
jilles

193

次のソリューションは、正規表現を必要とせず、Bourneなどの基本的なシェルでも使用できます。基本的に、非数値を使用した数値評価演算はエラーとなり、シェルでは暗黙的にfalseと見なされます。

"$var" -eq "$var"

のように:

#!/bin/bash

var=a

if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
  echo number
else
  echo not a number
fi

$をテストすることもできますか?より明示的な操作の戻りコード:

[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null
if [ $? -ne 0 ]; then
   echo $var is not number
fi

標準エラーのリダイレクトは、数値がない場合にbashが出力する「予想される整数式」メッセージを非表示にすることです。

警告(以下のコメントのおかげで):

  • 小数点付きの数値有効な「数値」として識別され
  • [[ ]]代わりに使用[ ]と、常に評価されますtrue
  • ほとんどの非Bashシェルは、この式を常に次のように評価します true
  • Bashの動作は文書化されていないため、警告なしに変更される可能性があります
  • 値の後にスペースが含まれている場合(例: "1 a")は次のようなエラーを生成します bash: [[: 1 a: syntax error in expression (error token is "a")
  • 値がvar-nameと同じ場合(例:i = "i")、次のようなエラーが発生します。 bash: [[: i: expression recursion level exceeded (error token is "i")

8
結果はBashで数値として使用できることが保証されているため、これは(ただし、変数は空の文字列を許可するように引用されています)でもお勧めします。
l0b0 2010

21
単一のブラケットを使用するように注意してください。[[ a -eq a ]]trueと評価されます(両方の引数がゼロに変換されます)
Tgr

3
非常に素晴らしい!これは整数に対してのみ機能し、数値に対しては機能しないことに注意してください。整数でなければならない単一の引数をチェックする必要があったので、これはうまくいきました:if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
haridsv

6
[組み込み関数が引数を算術として評価するシェルの数は少なくないため、この方法は強くお勧めしません。これは、ksh93とmkshの両方に当てはまります。さらに、どちらも配列をサポートしているため、コードインジェクションの機会が簡単にあります。代わりにパターンマッチを使用してください。
ormaaj 2014年

3
@ AlbertoZaccagni、bashの現在のリリースでは、これらの値は数値コンテキストルールでのみ解釈され、では解釈され[[ ]]ません[ ]。とは言っても、この動作はPOSIX標準testとbash独自のドキュメントの両方で指定されていません。bashの将来のバージョンでは、文書化された動作の約束を破ることなくkshに一致するように動作を変更する可能性があるため、現在の動作の持続に依存することは安全であるとは限りません。
Charles Duffy

43

これは、数値が非負の整数であり、両方ともシェルに依存せず(つまり、バシズムなし)、シェルの組み込みのみを使用するかどうかをテストします。

誤り。

この最初の回答(下記)では、最初の回答が変数の最初でない限り、文字を含む整数を使用できます。

[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";

正しい

以下のようjillesがでコメントや提案彼の答えこれは、シェル・パターンを使用して、それを行うための正しい方法です。

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";

5
これは正しく機能せず、数字で始まる文字列を受け入れます。$ {VAR ## WORD}などのWORDはシェルパターンであり、正規表現ではないことに注意してください。
jilles

2
その表現を英語に翻訳していただけませんか。私は本当にそれを使いたいのですが、bashのmanページを読んでも、それを信頼できるほど理解できません。
CivFan

2
*[!0-9]*少なくとも1つの数字以外の文字を含むすべての文字列に一致するパターンです。変数${num##*[!0-9]*}の内容を取得しnum、パターンに一致する最長の文字列を削除する「パラメーター拡張」です。パラメータ展開の結果が空(! [ -z ${...} ])でない場合は、数字以外の文字が含まれていないため、数値になります。
mrucci、

残念ながら、有効な数値でなくても、引数に数字がある場合、これは失敗します。たとえば、「exam1ple」や「a2b」などです。
studgeek 2017年

どちらも失敗122s :-(
Hastur 2018

40

誰もbashの拡張パターンマッチングを提案していません:

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"

5
グレン、私shopt -s extglobはあなたの投稿から削除しました(私が賛成したことは、ここで私のお気に入りの回答の1つです)。条件付き構成では次のように読み取れるからです:and 演算子を使用する==と、!=演算子の右側の文字列はパターンと見なされ、一致シェルオプションが有効にされているかのように、以下の「パターンマッチング」で説明されているルールに従いextglobます。よろしくお願いします!
gniourf_gniourf 2015

そのような状況では、する必要はありませんshopt extglob...それは知っておくと良いことです!
gniourf_gniourf 2015

単純な整数に適しています。

2
@Jdamian:そうです、これはBash 4.1で追加されました(2009年の終わりにリリースされました…Bash 3.2は2006年にリリースされました...今では旧式のソフトウェアになり、過去に行き詰まっている人には申し訳ありません)。また、extglobsがバージョン2.02(1998年にリリースされた)で導入され、<2.02バージョンでは機能しないと主張することもできます...ここでのコメントは、古いバージョンに関する警告として機能します。
gniourf_gniourf 2016

1
内の変数[[...]]は、単語分割またはグロブ拡張の対象ではありません。
グレンジャックマン

26

シェルで数値形式を直接解析するソリューションに驚いています。シェルはこれにはあまり適していません。ファイルとプロセスを制御するためのDSLです。少し下に、十分な数のパーサーがあります。次に例を示します。

isdecimal() {
  # filter octal/hex/ord()
  num=$(printf '%s' "$1" | sed "s/^0*\([1-9]\)/\1/; s/'/^/")

  test "$num" && printf '%f' "$num" >/dev/null 2>&1
}

'%f'を必要な特定の形式に変更します。


4
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
Gilles Quenot 2012

4
@sputnickお使いのバージョンは、元の関数に固有の(そして便利な)戻り値のセマンティクスを破壊します。だから、代わりに、単純にそのままの機能を残し、そしてそれを使用する:isnumber 23 && echo "this is a number" || echo "not a number"
マイケル・

4
これも持っているべき2>/dev/nullisnumber "foo"はないので、それはstderrを汚染しませんか?
gioele 2014

4
bashのような最新のシェルを「ファイルとプロセスを制御するためのDSL」と呼ぶことは、それらがそれ以上に使用されていることを無視しています-一部のディストリビューションでは、パッケージマネージャー全体とWebインターフェースが(醜いかもしれませんが)構築されています。ただし、変数を設定することさえ難しいため、バッチファイルは説明に適合します。
Camilo Martin

7
他の言語から慣用句をコピーして賢くしようとしているのはおかしいです。残念ながら、これはシェルでは機能しません。シェルは非常に特殊で、それらについての確かな知識がなければ、壊れたコードを書く可能性があります。コードが壊れています:isnumber "'a"trueを返します。これは、POSIX仕様に記載されています。先頭の文字が単一引用符または二重引用符の場合、値は、単一引用符または二重引用符に続く文字の基になるコードセットの数値になります。 。
gniourf_gniourf 2015

16

私は答えを見ていました... FLOAT数(ドット付き)について誰も考えていないことに気づきました!

grepの使用も素晴らしいです。
-Eは拡張正規表現を
意味します-qは静かな(エコーしない)ことを意味します
-qEは両方の組み合わせです。

コマンドラインで直接テストするには:

$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is: 32

$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is empty (false)

$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer .5

$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is 3.2

bashスクリプトでの使用:

check=`echo "$1" | grep -E ^\-?[0-9]*\.?[0-9]+$`

if [ "$check" != '' ]; then    
  # it IS numeric
  echo "Yeap!"
else
  # it is NOT numeric.
  echo "nooop"
fi

JUST整数に一致させるには、次のようにします。

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`

triple_rとtripleeeでawkを使用するソリューションは、浮動小数点で動作します。
ケンジャクソン

これをありがとう、とても良い点です!問題は、それが単なる整数ではなく数値であるかどうかを実際に確認する方法です。
タナシス

タナシスもありがとう!いつもお互いに助けましょう。
Sergio Abreu

12

@maryまでの単なるフォローアップ。しかし、十分な担当者がいないため、この投稿へのコメントとしてこれを投稿できませんでした。とにかく、これが私が使ったものです:

isnum() { awk -v a="$1" 'BEGIN {print (a == a + 0)}'; }

この関数は、引数が数値の場合は「1」を返し、それ以外の場合は「0」を返します。これは、浮動小数点数だけでなく整数でも機能します。使い方は次のようなものです:

n=-2.05e+07
res=`isnum "$n"`
if [ "$res" == "1" ]; then
     echo "$n is a number"
else
     echo "$n is not a number"
fi

4
数値を出力することは、終了コードを設定することほど役に立ちません。'BEGIN { exit(1-(a==a+0)) }'完全に理解するために少し難しいですが、同じように、trueまたはfalseを返す関数で使用することができ[grep -qなど、
tripleee

9
test -z "${i//[0-9]}" && echo digits || echo no no no

${i//[0-9]}の値の数字を$i空の文字列に置き換えますman -P 'less +/parameter\/' bash。を参照してください。-z結果の文字列の長さがゼロかどうかをチェックします。

$iが空の場合もケースを除外したい場合は、次の構文のいずれかを使用できます。

test -n "$i" && test -z "${i//[0-9]}" && echo digits || echo not a number
[[ -n "$i" && -z "${i//[0-9]}" ]] && echo digits || echo not a number

特にman -P 'less +/parameter\/' bashパーツに関しては高く評価しています。毎日何か新しいことを学ぶ。:)
デビッド

@sjas \-問題に対処するために正規表現を簡単に追加できます。[0-9\-\.\+]浮動小数点数と符号付き数値の計算に使用します。
user2683246

@sjas ok、私の過ち
-user2683246

@sjasecho $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
user2683246

9

私の問題では、ユーザーが誤ってテキストを入力しないようにする必要があるだけだったので、シンプルで読みやすいものにしようとしました

isNumber() {
    (( $1 )) 2>/dev/null
}

manページによると、これはほとんど私が望むことをします

式の値がゼロ以外の場合、戻り状況は0です。

「数字の可能性がある」文字列の厄介なエラーメッセージを防ぐために、エラー出力を無視します

$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")

これはisIntegerに似ています。しかし、本当にありがとう!
Naheel

8

古い質問ですが、私は自分の解決策に取り組みたかっただけです。これは奇妙なシェルトリックを必要としないか、永遠に存在しなかった何かに依存します。

if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
    echo 'is not numeric'
else
    echo 'is numeric'
fi

基本的に、それは入力からすべての数字を削除するだけであり、ゼロ以外の長さの文字列が残っている場合、それは数値ではありませんでした。


空の場合、これは失敗しますvar
gniourf_gniourf 2015

または、後続の改行またはのような変数の場合$'0\n\n\n1\n\n\n2\n\n\n3\n'
gniourf_gniourf 2015

7

まだコメントできないので、独自の回答を追加します。これは、bashパターンマッチングを使用したglenn jackmanの回答の拡張です。

私の本来の必要性は、数値を識別し、整数と浮動小数点数を区別することでした。以下に差し引かれる関数定義:

function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}

ユニットテスト(shUnit2を使用)を使用して、パターンが意図したとおりに機能することを検証しました。

oneTimeSetUp() {
    int_values="0 123 -0 -123"
    float_values="0.0 0. .0 -0.0 -0. -.0 \
        123.456 123. .456 -123.456 -123. -.456
        123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
        123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
        123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
}

testIsIntegerIsFloat() {
    local value
    for value in ${int_values}
    do
        assertTrue "${value} should be tested as integer" "isInteger ${value}"
        assertFalse "${value} should not be tested as float" "isFloat ${value}"
    done

    for value in ${float_values}
    do
        assertTrue "${value} should be tested as float" "isFloat ${value}"
        assertFalse "${value} should not be tested as integer" "isInteger ${value}"
    done

}

注:isFloatパターンは、小数点(@(.,))およびE記号(@(Ee))に対してより寛容になるように変更できます。単体テストでは、整数または浮動小数点の値のみをテストし、無効な入力はテストしません。


申し訳ありませんが、編集機能の使用方法がわかりませんでした。
3ronco 2016

6

私はこれを試します:

printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
    echo "$var is a number."
else
    echo "$var is not a number."
fi

注:これはnanおよびinfを数値として認識します。


2
pixelbeatの回答の複製か、コメントへの回答として適している(%fおそらくいずれにせよ、おそらく使用する方が良い)
michael

3
以前のステータスコードをチェックする代わりに、ifそれ自体を単に入れないのはなぜですか?それは何だif...んif printf "%g" "$var" &> /dev/null; then ...
カミロ・マーティン

3
これには他の警告があります。空の文字列やのような文字列を検証します'a
gniourf_gniourf 2015

6

明確な答えは、@ charles Dufyや他の人々によってすでに与えられています。純粋なbashソリューションは以下を使用します:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

実数の場合、基数ポイントの前に数値があることは必須ではありません

浮動小数点数と科学表記法のより完全なサポートを提供するには(C / Fortranの多くのプログラムまたはそうでなければfloatをこのようにエクスポートします)、この行への便利な追加は次のようになります:

string="1.2345E-67"
if [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

したがって、特定のタイプを探している場合は、数値のタイプを区別する方法につながります。

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+$ ]]
then
    echo $string is an integer
elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*$ ]]
then
    echo $string is a float
elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]]
then
    echo $string is a scientific number
else
    echo $string is not a number
fi

注:10進表記および科学表記の構文要件をリストできます。1つは、コンマを基点として使用できること、および「。」を使用できることです。次に、そのような基数ポイントは1つだけでなければならないと断言します。[Ee] floatには2つの+/-記号を含めることができます。Auluの作業からさらにいくつかのルールを学び、 '' '-' '-E-1' '0-0'などの不良ストリングに対してテストしました。以下は、我慢しているように見える私のregex / substring / exprツールです:

parse_num() {
 local r=`expr "$1" : '.*\([.,]\)' 2>/dev/null | tr -d '\n'` 
 nat='^[+-]?[0-9]+[.,]?$' \
 dot="${1%[.,]*}${r}${1##*[.,]}" \
 float='^[\+\-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$'
 [[ "$1" == $dot ]] && [[ "$1" =~ $float ]] || [[ "$1" =~ $nat ]]
} # usage: parse_num -123.456

6
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

-負の数を含めることを忘れないでください!


bashの最小バージョンは何ですか?私はただbashを取得します:条件付き2項演算子で予期されるbash:予期しないトークンに近い構文エラー `=〜 '
Paul Hargreaves

1
@PaulHargreaves =~は、少なくともbash 3.0まで存在していました。
Gilles「SO-邪悪なことをやめなさい」2014

@PaulHargreavesおそらく、最初のオペランドに問題がありました。たとえば、引用符が多すぎるなどです
Joshua Clayton

それは非常にですので、@JoshuaClayton私はバージョンについて尋ねた非常に我々はまだ持っていると、それは=〜サポートしていないSolaris 7のボックス、上の古いbashの
ポール・ハーグリーブス

6

これはgrep、問題の変数が拡張正規表現と一致するかどうかを確認するために使用することで実現できます。

テスト整数1120

yournumber=1120
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

出力: Valid number.

非整数をテストする1120a

yournumber=1120a
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

出力: Error: not a number.


説明

  • grep-Eスイッチは、私たちは拡張正規表現を使用することができます'^[0-9]+$'。この正規表現は、変数には変数の最初から最後まで[]0〜9の数字のみが含まれ、少なくとも1つの文字が含まれている必要があることを意味します。0-9^$+
  • grep-q静かなスイッチは、それが何かを見つけたかどうかを任意の出力がオフになります。
  • ifの終了ステータスをチェックしますgrep。終了ステータス0は成功を意味し、それ以上のものはエラーを意味します。grepコマンドはの終了ステータスを持っている0、それが一致するものを見つけた場合と1そうでないとき。

したがって、すべてをまとめて、ifテストではecho、変数$yournumber|パイプをgrep使用して、-qスイッチを使用して-E拡張された正規表現'^[0-9]+$'式に暗黙的に一致させます。終了ステータスはgrepなります0場合はgrep、正常マッチを発見し、1それがなかった場合。成功した場合、私たちは、一致しますecho "Valid number."。それが一致しなかった場合は、私たちecho "Error: not a number."


フロートまたはダブルスの場合

私達はちょうどから正規表現を変更することができます'^[0-9]+$'への'^[0-9]*\.?[0-9]+$'山車やダブルスのために。

テストフロート1120.01

yournumber=1120.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

出力: Valid number.

テストフロート11.20.01

yournumber=11.20.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

出力: Error: not a number.


ネガティブ

負の整数を許可するには、正規表現をから'^[0-9]+$'に変更するだけ'^\-?[0-9]+$'です。

負の浮動小数点数または倍精度浮動小数点数を許可するには、正規表現をから'^[0-9]*\.?[0-9]+$'に変更するだけ'^\-?[0-9]*\.?[0-9]+$'です。


その通りです、@ CharlesDuffy。ありがとう。答えを整理しました。
ジョセフシー

正解です、@ CharlesDuffy。修繕!
ジョセフシー

1
LGTM; 編集済みの回答には+1があります。この時点で私が異なることをする唯一のことは、正確さではなく意見の問題だけです(f / e、[-]代わりに\-との[.]代わりに使用することは\.少し冗長ですが、文字列を変更する必要がないことを意味しますバックスラッシュが消費されるコンテキストで再使用されます)。
Charles Duffy

4

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html

bashの文字クラスを使用することもできます。

if [[ $VAR = *[[:digit:]]* ]]; then
 echo "$VAR is numeric"
else
 echo "$VAR is not numeric"
fi

数値には、スペース、小数点、浮動小数点の「e」または「E」が含まれます。

ただし、Cスタイルの16進数、つまり「0xffff」または「0XFFFF」を指定すると、[[:digit:]]はtrueを返します。ここでちょっとした罠ですが、bashを使用すると、「0xAZ00」のようなことを行っても、それを数字として数えることができます(これは、16以外のベースに0x表記を使用できるようにするGCCコンパイラの奇妙な癖からではありませんか??? )

16進数を受け入れたくない場合を除いて、入力が完全に信頼できない場合、数値かどうかをテストする前に、「0x」または「0X」をテストすることをお勧めします。それは以下によって達成されます:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi

17
[[ $VAR = *[[:digit:]]* ]]変数整数の場合でなく数値を含む場合、trueを返します。
グレン・ジャックマン2012年

[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"プリントnumeric。bashバージョンでテスト済み3.2.25(1)-release
Jdamian

1
@ultraswadable、ソリューションは、少なくとも1つの数字が他の文字で囲まれた(または囲まれていない)文字列を検出します。私は反対票を投じました。
Jdamian

したがって、明らかに正しいアプローチはこれを逆にして使用することです[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz

@eschwartz、あなたのソリューションは負の数では機能しません
エンジェル

4

最も簡単な方法は、数字以外の文字が含まれているかどうかを確認することです。すべての数字を何も置換せず、長さをチェックします。長さがあれば数字ではありません。

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi

2
負の数を処理するには、より複雑なアプローチが必要になります。
アンディ

...またはオプションの正符号。
tripleee 2017

@tripleeeあなたがそれを行う方法を知っているなら、私はあなたのアプローチを見たいです。
アンディ

4

私は最近これを改ざんしなければならなかったので、ユニットテストでのkarttuのアプローチが最も好きです。私はコードを修正し、他のいくつかの解決策も追加しました。自分で試して結果を確認してください。

#!/bin/bash

    # N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
 [[ ${1} =~ ^[0-9]+$ ]]
}
    # Z={...,-2,-1,0,1,2,...} by karttu
function isInteger() 
{
 [[ ${1} == ?(-)+([0-9]) ]]
}
    # Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat() 
{
 [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
    # R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
 isNaturalNumber $1 || isInteger $1 || isFloat $1
}

bools=("TRUE" "FALSE")
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
    123.456 123. .456 -123.456 -123. -.456 \
    123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
    123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
    123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
false_values="blah meh mooh blah5 67mooh a123bc"

for value in ${int_values} ${float_values} ${false_values}
do
    printf "  %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
    printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
    printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
    printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done

そのため、isNumber()にはダッシュ、コンマ、指数表記が含まれているため、整数と浮動小数点数ではTRUEを返し、一方でisFloat()は整数値でFALSEを返し、isInteger()は同様に浮動小数点数ではFALSEを返します。ワンライナーとしてあなたの便宜のために:

isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; }
isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }

個人的にはfunction何の役にも立たないので、キーワードを削除します。また、戻り値の有用性についてもわかりません。特に指定がない限り、関数は最後のコマンドの終了ステータスを返すので、return自分で何もする必要はありません。
トムフェネック2016

確かに、returnsは紛らわしく、読みにくくなります。functionキーワードを使用するかどうかは、少なくとも個人的な趣味の問題です。少なくともスペースを節約するために、ライナーからそれらを削除しました。THX。
3ronco 2016

1行バージョンのテストの後にセミコロンが必要であることを忘れないでください。
トムフェネック2016

2
isNumberは、数値が含まれる文字列に対して「true」を返します。
DrStrangepork 2016年

@DrStrangepork確かに、私のfalse_values配列にはそのケースがありません。調べてみます。ヒントをありがとう。
3ronco

4

私はexprを使用します。数値以外の値にゼロを追加しようとすると、ゼロ以外の値が返されます。

if expr -- "$number" + 0 > /dev/null 2>&1
then
    echo "$number is a number"
else
    echo "$number isn't a number"
fi

非整数が必要な場合はbcを使用することが可能かもしれませんが、私bcはまったく同じ動作をするとは思いません。非数値にゼロを追加すると、ゼロが取得され、ゼロの値も返されます。たぶん、あなたは組み合わせることができますbcexprbcにゼロを追加するために使用します$number。答えがの場合は0、それがゼロでないexprことを確認してください$number


1
これはかなり悪いです。少し良くするには、以下を使用する必要がありますexpr -- "$number" + 0。まだこれはまだそのふりをし0 isn't a numberます。からman exprExit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf

3

アルベルト・ザッカニの答えが好きです。

if [ "$var" -eq "$var" ] 2>/dev/null; then

重要な前提条件:-生成されるサブシェルがない-REパーサーが呼び出されない-ほとんどのシェルアプリケーションは実数を使用しません

しかし、$var複雑な場合(例:連想配列アクセス)、数値が負でない整数(ほとんどのユースケース)になる場合、これはおそらくより効率的ですか?

if [ "$var" -ge 0 ] 2> /dev/null; then ..

2

形式文字列 "%f"または "%i"を指定した場合、printfがチェックを実行します。チェックを再発明するよりも簡単で、構文は単純で短く、printfは至る所にあります。したがって、私の意見ではそれはまともな選択です。次のアイデアを使用して、さまざまなものをチェックすることもできます。数値をチェックするだけでなく、

declare  -r CHECK_FLOAT="%f"  
declare  -r CHECK_INTEGER="%i"  

 ## <arg 1> Number - Number to check  
 ## <arg 2> String - Number type to check  
 ## <arg 3> String - Error message  
function check_number() { 
  local NUMBER="${1}" 
  local NUMBER_TYPE="${2}" 
  local ERROR_MESG="${3}"
  local -i PASS=1 
  local -i FAIL=0   
  case "${NUMBER_TYPE}" in 
    "${CHECK_FLOAT}") 
        if ((! $(printf "${CHECK_FLOAT}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
    "${CHECK_INTEGER}") 
        if ((! $(printf "${CHECK_INTEGER}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
                     *) 
        echo "Invalid number type format: ${NUMBER_TYPE} to check_number()." 1>&2
        echo "${FAIL}"
        ;;                 
   esac
} 

>$ var=45

>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }


2

正規表現 vs パラメータ拡張

チャールズ・ダフィーの答えの仕事が、唯一の使用正規表現、これが遅いことがわかっているので、別の方法を使用して、パラメータ展開

正の整数のみ:

is_num() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;}

整数の場合:

is_num() { 
    local chk=${1#[+-]};
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}

次に、フローティングの場合:

is_num() { 
    local chk=${1#[+-]};
    chk=${chk/.}
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}

最初のリクエストに一致させるには:

set -- "foo bar"
is_num "$1" && VAR=$1 || echo "need a number"
need a number

set -- "+3.141592"
is_num "$1" && VAR=$1 || echo "need a number"

echo $VAR
+3.141592

ここで少し比較します。

Charles Duffyの回答に基づく同じチェックがあります

cdIs_num() { 
    local re='^[+-]?[0-9]+([.][0-9]+)?$';
    [[ $1 =~ $re ]]
}

いくつかのテスト:

if is_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number

OK、それは大丈夫です。これすべてにかかる時間はどれくらいですか(私のラズベリーpiで):

time for i in {1..1000};do is_num +3.14159265;done
real    0m2.476s
user    0m1.235s
sys     0m0.000s

次に、正規表現で:

time for i in {1..1000};do cdIs_num +3.14159265;done
real    0m4.363s
user    0m2.168s
sys     0m0.000s

とにかく、パラメーター拡張を使用できる場合は、正規表現を使用しない方がいいと思います... REを悪用するとbashスクリプトが遅くなります
F.ハウリ

編集された回答:必要はありませんextglob(そしてもう少し早く)!
F.ハウリ

@CharlesDuffy、私のラズベリーpiでは、1000回の反復には、私のバージョンでは2.5秒、バージョンでは4,4秒かかります。
F.ハウリ

それと議論することはできません。👍
チャールズダフィー

1

負の数をキャッチするには:

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi

また、これにはまず拡張グロビングを有効にする必要があります。これはBashのみの機能で、デフォルトでは無効になっています。
tripleee 2017

==または!= When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled. gnu.org/software/bash/manual/bashref.html#index-_005b_005b
Badr

@BadrElmers更新ありがとうございます。これは、私のBash 3.2.57(MacOS Mojave)では当てはまらない新しい動作のようです。4.4で説明したように動作します。
Tripleee

1

「let」も次のように使用できます。

[ ~]$ var=1
[ ~]$ let $var && echo "It's a number" || echo "It's not a number"
It\'s a number
[ ~]$ var=01
[ ~]$ let $var && echo "It's a number" || echo "It's not a number"
It\'s a number
[ ~]$ var=toto
[ ~]$ let $var && echo "It's a number" || echo "It's not a number"
It\'s not a number
[ ~]$ 

しかし、私はこのスレッドのいくつかの回答のように、「=〜」Bash 3+演算子を使用することを好みます。


5
これは非常に危険です。シェルで未検証の演算を評価しないでください。最初に別の方法で検証する必要があります。
ormaaj 2014年

1
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

-\?負の整数を受け入れない場合は、grep一致パターンを削除します。


2
説明の欠如に対する反対投票。これはどのように作動しますか?複雑で壊れやすいように見え、どの入力が正確に受け入れられるかは明らかではありません。(たとえば、スペースを削除することが非常に必要ですか?なぜですか?スペースが埋め込まれた数値は有効な数値であり、望ましくない場合があります。)
tripleee

1

ドットで区切られた部分全体と小数部分をテストする正規表現を使用して、同じことをここで行いましたか?

re="^[0-9]*[.]{0,1}[0-9]*$"

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi

あなたの答えが他の古い答え、例えばチャールズ・ダフィーの答えと根本的に異なる理由を説明していただけますか?さて、あなたの答えは単一の期間を検証するので実際には壊れています.
gniourf_gniourf

ここで単一の期間を理解することは確かではありません...期待される1または0の期間です...しかし、根本的に何も違いはありません。
ジェローム

また、*を使用すると、より現実的なケースに一致するはずです
ジェローム

空の文字列a=''とピリオドのみを含む文字列を一致させているa='.'ため、コードが少し壊れています...
gniourf_gniourf

0

以下を使用します(整数の場合):

## ##### constants
##
## __TRUE - true (0)
## __FALSE - false (1)
##
typeset -r __TRUE=0
typeset -r __FALSE=1

## --------------------------------------
## isNumber
## check if a value is an integer 
## usage: isNumber testValue 
## returns: ${__TRUE} - testValue is a number else not
##
function isNumber {
  typeset TESTVAR="$(echo "$1" | sed 's/[0-9]*//g' )"
  [ "${TESTVAR}"x = ""x ] && return ${__TRUE} || return ${__FALSE}
}

isNumber $1 
if [ $? -eq ${__TRUE} ] ; then
  print "is a number"
fi

ほぼ正しいです(空の文字列を受け入れます)が、難読化するほどに複雑になっています。
Gilles 'SO-悪をやめる

2
不正解:(-nなどのためecho)などを受け入れており、(のため)末尾に改行のある変数を受け入れています$(...)。ところで、printは有効なシェルコマンドではありません。
gniourf_gniourf 2015

0

ウルトラソーブレードのレシピを試してみました。私にとっては最も実用的であり、機能させることができなかったためです。最後に、私は別の方法を考案しましたが、他のパラメータ置換に基づいて、今回は正規表現の置換を行いました。

[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"

$ var内のすべての:digit:クラス文字を削除し、空の文字列が残っているかどうかをチェックします。つまり、元の文字列は数字のみだったということです。

これについて私が好きなのは、その小さな設置面積と柔軟性です。この形式では、区切られていない、基数10の整数に対してのみ機能しますが、パターンマッチングを使用して他のニーズに合わせることができます。


mrucciのソリューションを読むと、私とほぼ同じように見えますが、「sedスタイル」の代わりに通常の文字列置換を使用しています。どちらもパターンマッチングに同じルールを使用し、AFAIKは互換性のあるソリューションです。
22:41

sedPOSIXですが、ソリューションはbashです。どちらにも用途があります
v010dya

0

Quick&Dirty:最もエレガントな方法ではないことはわかっていますが、通常はゼロを追加して結果をテストします。そのようです:

function isInteger {
  [ $(($1+0)) != 0 ] && echo "$1 is a number" || echo "$1 is not a number"
 }

x=1;      isInteger $x
x="1";    isInteger $x
x="joe";  isInteger $x
x=0x16 ;  isInteger $x
x=-32674; isInteger $x   

$(($ 1 + 0))は、$ 1が整数でない場合、0または爆弾を返します。例えば:

function zipIt  { # quick zip - unless the 1st parameter is a number
  ERROR="not a valid number. " 
  if [ $(($1+0)) != 0 ] ; then  # isInteger($1) 
      echo " backing up files changed in the last $1 days."
      OUT="zipIt-$1-day.tgz" 
      find . -mtime -$1 -type f -print0 | xargs -0 tar cvzf $OUT 
      return 1
  fi
    showError $ERROR
}

注:スクリプト全体を爆弾にする浮動小数点数または混合型をチェックすることは考えていなかったと思います...私の場合、それ以上進めたくありませんでした。私は、mrucciのソリューションとDuffyの正規表現で遊んでみます-それらはbashフレームワーク内で最も堅牢に見えます...


4
これは1+1、のような算術式を受け入れますが、先頭に0s があるいくつかの正の整数を拒否します(これ08は、無効な8進定数であるため)。
Gilles 'SO-悪をやめる'

2
これには他の問題もあります:0は番号ではなく、任意のコードインジェクションの影響を受けるため、試してください:isInteger 'a[$(ls)]'。おっと。
gniourf_gniourf 2015

1
との展開$((...))は引用符で囲まれていない、数値IFS=123はそれを変更します。
Isaac
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.