私はシェル スクリプトの初心者で、基本を学ぼうとしています。ターミナルを使用して、「./scriptgoeshere.sh Argument1 Argument2」などのスクリプトを起動しています。
スクリプトでエコーを出力したいのですが、「Hello World」がおそらく最も適した出力でしょう。ただし、次の場合にのみ hello world を出力したいです: 正確に 2 つの引数があり、それらの引数は数字のみで構成されている必要があります。例:
./scriptgoeshere.sh 1523 9643 ./scriptgoeshere.sh 7 96 は動作するはずですが、 ./scriptgoeshere.sh 594 arg は動作しないはずです。これは現時点で私が持っているスクリプトですが、動作しません。一日中これを理解しようとしましたが、うまくいかないようです。if 部分に 2 つの入力があるかどうか、そして続行する前に elif で両方が数値であるかどうかを確認する if/elif/else も試しました。
#!/bin/bash
if [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ];
then
i=0
while [ $i -lt 3 ]
do
echo "Hello World!"
sleep 5
i=$((i+1))
done
else
echo "Need two arguments and only numbers will be accepted, such as ./scriptgoeshere 153 251"
fi
2 つの引数を入力するまで hello world ループがブロックされますが、引数が数値かテキストかは関係なく、次のエラーが発生します: "./scriptgoeshere.sh: 行 2: [: ^[0-9]: 整数式が必要です"。ターミナルで ./scriptgoeshere.sh 123 123 と入力して起動しても、このエラーが発生します。実際のスクリプトを起動するときに引数を把握したいので、変数で解決したくないのです。
頭がおかしくなりそうだよ!
これは、正確に 2 つの引数があるかどうかをチェックして動作させるために始めたものです。これに小さな修正を加えて、その 2 つの引数が必ず数値になるようにしたいだけです。なぜこんなに難しいのかわかりません。
#!/bin/bash
if [ "$#" -eq 2 ];
then
i=0
while [ $i -lt 3 ]
do
echo "Hello World!"
sleep 1
i=$((i+1))
done
else
echo "Need two argument, only numbers accepted"
fi
編集: 解決しました。かなり近づきました。if 文字列の数字部分に [] を追加するだけで済みました。最初の部分は [] を 1 つだけ使用して記述できるのに、なぜ [[ "$@" -eq ^[0-9] ]] と記述する必要があるのか、誰か知っていますか?
答え1
最初は([ "$#" -eq ^[0-9] ]
または[ "$@" -eq ^[0-9] ]
、回答を作成中に質問が編集されました…)。文脈から判断すると、パラメータが数値かどうかをテストしたいのだと思います。
$#
は常に数値で、位置パラメータの数は 10 進数で表されます。$1
と という2 つの位置パラメータをテストします$2
。[
はコマンドです ([ whatever ]
は と同等ですtest whatever
)。これはシェルの組み込みコマンドである可能性がありますが、構文的にはls
またはに似ていますman
。 に続く単語 ( を含む]
) は、コマンドに続く単語と同様にシェルによって解析されます。シェルはそれらを展開し (たとえば、 は$#
数値になります)、引用符を削除します。最終的には のパラメータになります[
。これらの展開の 1 つにファイル名展開があり、さまざまなパターンが既存のファイル名と照合されます (ファイル名展開、グロブ)。これらのファイルが存在する場合、引用符なし^[0-9]
は に展開されることがあります^2 ^5
。一致がない場合、または機能を無効にしている場合は、 が(その引数の 1 つとして) 文字通りに^[0-9]
なります[
。これはパターンのように見えますが、[
パターン マッチングはサポートされていません。-eq
は、前の引数と次の引数が整数であることを必要とします。 は確実に整数に展開されます。このコンテキストでは、 を数値に展開する$#
方法はありません。^[0-9]
二重引用符は
$@
特別なもので、引用符で囲まれているにもかかわらず、複数の単語 (POSIX では「フィールド」) に展開できます。通常、単語の分割を防ぐために引用符を使用します"$foo"
。 は、値にスペースなどが含まれていても 1 つの単語に展開されます。"$@"
は のように展開され"$1" "$2" …
、その二重引用符によって$1
が複数の単語 (および別々など) に展開されるのを防ぎます$2
。このフラグメントでは、[ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]
2番目
[
は、1番目が成功した場合(これが&&
動作の仕組み)、つまり位置パラメータがちょうど2つある場合に実行されます。このような場合、"$@"
はと同等であり"$1" "$2"
、2番目のテストは次のようになります。[ "$1" "$2" -eq ^[0-9] ]
これは意味をなさない。
[
パラメータの実際の値と の展開に応じてさまざまな方法で文句を言うかもしれませんが^[0-9]
、それでもまだゴミです。
さて、 について[[ "$#" = [0-9] ]]
。
注: このスニペットは質問の最初の改訂版にありました。質問は私が回答を作成している間に編集されました。[[
いずれにせよ、この断片はいずれ役に立つかもしれないので、残しておくことにしました。
- 繰り返しますが、
$#
常に数値です。 [[
は と同等ではありません[
。 で実行できる正常なテストは、[
で書き直すことができます(ただし、を に[[
置き換えるだけの単純な操作は行わないでください)。 で実行できる追加のテストがあり、 の方が強力です。(および) は POSIX で必須ですが、は必須ではありません。[
[[
[[
[
test
[[
重要な点:は や の
[[
ようなコマンドではありません。これはキーワードであり、シェルが後続の単語を解析する方法を変更します。との間にあるすべてのものは、異なる方法で解析されます。ls
[
[[
]]
演算子が の場合
=
、次の引数はパターンです。一部の文字/構文 (例:*
または[0-9]
) は、引用符で囲まれていない場合、 の前の引数と照合されます=
。これはファイル名の展開に似ていますが、既存のファイル名とは一致しません。コードでは、(展開された)$#
がちょうど 1 桁の数字であるかどうかをテストしました。テスト全体は[[ "$#" -eq 2 ]] && [[ "$#" = [0-9] ]]
foo && bar
bar
が成功した場合のみ、が実行されfoo
ます。2 番目はに拡張された[[
場合にのみテストされるため、 はちょうど 1 桁の数字であるため、成功する必要があります。この場合、2 番目は何も変更しません。$#
2
2
[[
間違いに気づき、 が$1
数値かどうかをテストしたとします。
- 上記のように、ちょうど 1 桁に拡張された
[[ "$1" = [0-9] ]]
場合は成功を返します。$1
[[ "$1" = [0-9][0-9] ]]
– ちょうど 2 桁です。[[ "$1" = [0-9]* ]]
– 数字で始まるもの。
任意の数の数字を検出するには が必要です=~
。内部のこの演算子[[ ]]
は と多少似ていますが=
、パターンは拡張正規表現です。ただし、演算子の左側の文字列全体と一致する必要はありません。部分文字列で十分です (したがって、この場合は^
とアンカーが必要です)。$
[[ "$1" =~ [0-9] ]]
$1
数字を含む文字列に展開された場合は成功を返します。[[ "$1" =~ [0-9][0-9] ]]
– … 隣り合った 2 つの数字が含まれます。[[ "$1" =~ ^[0-9]$ ]]
– ちょうど 1 桁の数字からなる文字列。[[ "$1" =~ ^[0-9]*$ ]]
– 0 個以上の数字。[[ "$1" =~ ^[0-9][0-9]*$ ]]
– 1 桁以上の数字。[[ "$1" =~ ^[0-9]+$ ]]
– 1 桁以上の数字。
[[ "$1" =~ ^[0-9]+$ ]]
これらは[[ "$2" =~ ^[0-9]+$ ]]
あなたが望むかもしれないテストです。先頭を許可するには、-
次のように変更します。[[ "$1" =~ ^-?[0-9]+$ ]]
テストフラグメント全体は
[ "$#" -eq 2 ] && `[[ "$1" =~ ^-?[0-9]+$ ]]` && `[[ "$2" =~ ^-?[0-9]+$ ]]`
内部[[ ]]
変数/パラメータは二重引用符で囲むことはできません。また、展開された値は単語分割やグロブ化されません。したがって、記述して[[ $1 =~ ^-?[0-9]+$ ]]
も安全です。これは例外です。一般的には、変数は二重引用符で囲む必要があります。ここでは二重引用符で囲んでも何も問題はありません。したがって、この一般的な良い慣習に慣れるために、とにかく引用符で囲むことをお勧めします。
あなたは、 を使用して問題を解決したと主張しています[[ "$@" -eq ^[0-9] ]]
。申し訳ありませんが、信じられません。位置パラメータが 2 つあり、それらが数値である場合、これが構文的に正しい方法はわかりません。ましてや、あなたが望むものをテストすることはできません。他のケースでは...まだ方法がわかりません。
… を使用して[[ "$@" =~ ^[0-9] ]]
(コメントから引用、質問と矛盾)。これは適切な解決策ではありません。私のテストでは、"$@"
の内部は、単語の分割やグロブなしで 1 つの単語として扱われる[[ ]]
引用符なしの動作をします$@
。これは、 の内部で通常の変数がどのように動作するかと非常によく似ています[[ ]]
が、 が通常の変数とは異なることを知っていたため、事前には明らかではありませんでした"$@"
。
補足: この理由から、私は"$@"
insideを使いたくありませんでした[[ ]]
。それが正しいシナリオを想像できません。
位置パラメータがちょうど 2 つある場合、「ソリューション」は になります[[ "$1 $2" =~ ^[0-9] ]]
。これは構文的に有効であり、スクリプトの最初の引数の最初の文字が数字であるかどうかをチェックします。これだけです。 を実行すると./scriptgoeshere.sh 0h well
、引数はテストに合格します。
ノート:
[0-9]
(グロブパターンまたは正規表現として) は、現在のロケールに依存します。 ほとんどの場合、期待どおりに動作しますが、技術的には が9
先行するロケール0
や が2
の後ろに位置するロケールを持つことが可能です0-9
。 数字に一致させる最も一般的な方法は です[[:digit:]]
。これ。一方、
$1
and/or を$2
シェルの算術演算 ($((…))
)[ "$1" -ge 50 ]
などと一緒に使用したい場合もあります。その場合、必要なのはアラビア数字だけです。 が[[:digit:]]
ロケールでそれらを含んでいる場合でも (おそらくそうでしょう)、他の数字も含まれている可能性があります。 を検討してください[0123456789]
。これは明示的であり、ロケールに依存しません。and/or
^-?[0-9]+$
の観点からは、一致するすべての文字列が整数であるとはみなされません(たとえば、 を実行する場合)。各ツールには、処理できる整数の範囲があります。同様に、範囲外の整数を表す数字の文字列を入力すると、シェルの算術演算によって数学的に誤った結果が生成される場合があります。[
[[
[ "$1" -eq 0 ]
数学的には
1.00
は整数です。ツールによっては整数として受け入れるものもあれば、受け入れないものもあります。 はどうでしょうか1e100
?