十分に一般的なことをしようとしています:シェルスクリプトでユーザー入力を解析します。ユーザーが有効な整数を指定した場合、スクリプトは1つのことを行い、無効な場合は別のことを行います。問題は、これを行うための簡単な(そしてかなりエレガントな)方法を見つけていないことです。文字ごとに区別する必要はありません。
これは簡単なはずですが、方法はわかりません。私は何十もの言語でそれをすることができましたが、BASHではできませんでした!
私の研究でこれを見つけました:
文字列が基数10の有効な実数で構成されているかどうかをテストする正規表現
そしてそこには正規表現について話す答えがありますが、私が知る限り、それはCで利用可能な関数です(とりわけ)。それでも、それは素晴らしい答えのように見えたので、grepで試しましたが、grepはそれをどうするかわかりませんでした。私は-Pを試しましたが、これは私のボックスではPerl正規表現として扱うことを意味しています-nada。ダッシュ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
[[ $var =~ ^-?[0-9]+$ ]]
^
は、入力パターンの始まりを示します-
はリテラル「-」です?
は、「前の0または1(-
)」を意味します+
は、「1つ以上の先行する([0-9]
)」を意味します$
は、入力パターンの終わりを示しますしたがって、正規表現は、オプションの-
(負の数の場合)に一致し、その後に1つ以上の10進数が続きます。
参照:
うわー...ここにはとても良い解決策がたくさんあります!!上記のすべてのソリューションの中で、-eq
ライナーを1つ使用することが最もクールであることに@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]+$ ]]
ここでパーティーの後発。最も簡単で、最速で、最も移植性の高いソリューションについて言及している回答がないことに、私は非常に驚いています。 case
ステートメント。
case ${variable#[-+]} in
*[!0-9]* | '') echo Not a number ;;
* ) echo Valid number ;;
esac
比較前の記号のトリミングは、ちょっとしたハックのように感じますが、それにより、caseステートメントの式が非常に簡単になります。
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 : REGEX
は、STRINGの先頭にアンカーされたREGEXを検索し、最初のグループ(または一致しない場合は一致の長さ)をエコーし、成功/失敗を返します。これは古い正規表現構文であるため、余分な\
です。 -\?
は「多分-
」を意味し、[0-9]\+
は「1桁以上」を意味し、$
は「文字列の終わり」を意味します。
Bashは拡張グロブもサポートしていますが、どのバージョンから先のことは思い出せません。
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])) ]]
@(-|)
は「-
or nothing」、[0-9]
は「数字」、*([0-9])
は「ゼロ以上の数字」を意味します。
基本的には1行なので、-eq
テストを使用したソリューションが気に入っています。
私自身の解決策は、パラメーター展開を使用してすべての数字を破棄し、何か残っているかどうかを確認することでした。 (私はまだ3.0を使用しており、以前に[[
またはexpr
を使用したことはありませんが、それらに会えてうれしいです。)
if [ "${INPUT_STRING//[0-9]}" = "" ]; then
# yes, natural number
else
# no, has non-numeral chars
fi
さらにもう1つ考えてみましょう(組み込みコマンドtestとその戻りコードのみを使用):
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
数字以外を削除して比較することができます。デモスクリプトは次のとおりです。
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非整数
または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
笑いのために、私はこれを行うための一連の関数(is_string、is_int、is_float、is alpha文字列、またはその他)を大まかにすばやく計算しましたが、これを行うためのより効率的な(コードが少ない)方法があります:
#!/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 + 0
またはsed)
Ignacio Vazquez-Abramsからの回答に追加します。これにより、整数の前に+記号を付けることができ、任意の数のゼロを小数点として使用できます。たとえば、これにより+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