よし、 Jolt Colaの缶を手に入れて座ってくれ。これにはしばらく時間がかかります...
Unix シェルのif
ステートメントは、コマンド (任意のコマンド) を受け取り、そのコマンドを処理し、そのコマンドがゼロの終了コードを返す場合、 ifステートメントのthen節を実行します。そのコマンドがゼロ以外の値を返す場合、else句があればそれを実行します。
例えば:
if ls foo 2>&1 > /dev/null
then
echo "File 'foo' exists"
else
echo "No Foo For you!"
fi
上記はls foo
コマンドを実行します。出力は を介してドロップされる2>&1 > /dev/null
ため、リストが存在する場合は表示されず、存在しないfoo
場合はエラー メッセージが表示さfoo
れません。ただし、存在する場合はls
コマンドが返され、存在しない場合は a が返されます。0
foo
1
これにより、あらゆる種類のきちんとしたことができます。
if mv foo foo.backup
then
echo "Renamed `foo` directory to `foo.backup`"
else
echo "BIG HORKING ERROR: Can't rename 'foo' to 'foo.backup'"
exit 2 #I need to stop my program. This is a big problem.
fi
上記のコードでは、新しいディレクトリを作成foo
する前に、ディレクトリの名前を に変更しようとしています。おそらくディレクトリが存在しないため、コマンドは失敗します。ディレクトリの名前を変更する権限がない可能性があります。それが何であれ、このステートメントにより、コマンドが成功したかどうかをテストできます。foo.backup
foo
foo
mv
if
mv
これがif
コマンドのすべてです。コマンドを実行し、戻り値がゼロかどうかに応じて、then句を実行する場合と実行しない場合があります。
しかし、特定の条件が存在するかどうかをテストしたい場合はどうすればよいでしょうか? 例えば:
- というディレクトリはあり
foo
ますか?
- 環境変数の
$value
値は 3 より大きいですか?
これらのことをテストできるようなコマンドがあればいいと思いませんか? ねえ、私のテストが真の場合にそのコマンドがゼロ値を返し、私のテストが偽の場合にゼロ以外の値を返す場合、それをif
ステートメントに組み込むことができます!
幸いなことに、Unix にはtestと呼ばれるコマンドがあり、まさにこれを実行します。
if test -d foo
then
echo "Directory foo exists"
fi
if test $value -gt 3
then
echo "\$value is bigger than 3"
fi
また、Unix コマンドにコマンドへのtest
ハードリンク[
がある場合もあります。
$ cd /bin
$ ls -li test [
54008404 -rwxr-xr-x 2 root wheel 18576 Jul 25 2012 [
54008404 -rwxr-xr-x 2 root wheel 18576 Jul 25 2012 test
最初の列はinode番号です。どちらも同じ inode 番号であるため、[
コマンドは単にコマンドへのハード リンクtest
です。
これで、物事が少し理解しやすくなりました。1は実際には[
Unixコマンドです! そのため、角括弧とテスト対象の間にスペースが必要です。
# Invalid Syntax: I need spaces between the test and the braces:
if [$value -gt 3]
# Valid Syntax
if [ $value -gt 3 ]
# Actually the same command as above
if test value -gt 3
また、単にorではなく、先頭にダッシュが付いている理由がより明確に-gt
なります。これは、 andが test コマンドのパラメーターであるためです。-f
gt
f
-gt
-f
組み込みのシェルがどのように機能するif
か、および[
が実際にどのように Unix コマンドであるかをより明確に理解できたので、if ステートメントを見て、問題が何であるかを確認できます。
if [ "-f "/data/sb3???sh25t.sqfs" && "! -f /data/sb3??????sh25t.sqfs" && \
"! -f /data/sb3????????sh25t.sqfs" && "! -f /data/sb3???????sh25t.sqfs" ]
まず、次のものがあります。
"-f /data/sb3???sh25t.sqfs"
引用符は、文字列全体をtestコマンドの 1 つのパラメーターにしますが、これは必要なものではありません。-f
テストしているファイルとは別のパラメーターが必要です。-f
したがって、引用符からを削除する必要があるため、 testコマンドはそれをパラメーターとして認識します。
if [ -f "/data/sb3???sh25t.sqfs" && ! -f "/data/sb3??????sh25t.sqfs" && \
! -f "/data/sb3????????sh25t.sqfs" && ! -f "/data/sb3???????sh25t.sqfs" ]
これはまだ機能しません。コマンドについては、(man ページ)[http://www.manpagez.com/man/1/test/] を参照してくださいtest
。&&
はコマンドの有効なパラメーターではないことに注意してくださいtest
。代わりに、-a
2 つの式を組み合わせるために使用することを想定しています_ テスト:
if [ -f "/data/sb3???sh25t.sqfs" -a ! -f "/data/sb3??????sh25t.sqfs" -a \
! -f "/data/sb3????????sh25t.sqfs" -a ! -f "/data/sb3???????sh25t.sqfs" ]
then
は引用符の-f
外にあり (そして も!
)、-a
to と 4 つの条件すべてを一緒に使用しています。
次のようなこともできます。
if [ -f "/data/sb3???sh25t.sqfs" ] && [ ! -f "/data/sb3??????sh25t.sqfs" ] && \
! -f "/data/sb3????????sh25t.sqfs" ] && [ ! -f "/data/sb3???????sh25t.sqfs" ]
これは、実際にはlist operator&&
と呼ばれる bash シェル構造です。2 つの個別のコマンドをリンクし、次のことを行います。
- 最初のコマンドを実行します。
- 最初のコマンドがゼロ以外の値を返す場合は、2 番目のコマンドを実行します。それ以外の場合は、最初のコマンドの終了コードを返し、2 番目のコマンドを実行しないでください。
たとえば、次のようなものをよく見かけます。
[ -d foo ] && rm -rf foo
または(同じこと):
test -d foo && rm -rf foo
foo
上記のコマンドでは、ディレクトリとして存在するかどうかをテストしています。存在する場合、テスト コマンドは true 値を返し、ディレクトリを削除する 2 番目のコマンドを実行します。それは同じです...
if [ -d foo ]
then
rm -rf foo
fi
したがって、私のifステートメントは 4 つの個別のコマンドで構成されています。最初の[...]
コードが実行され、ファイル/data/sb3???sh25t.sqfs
が実際に存在するかどうかが確認されます。そうであれば、そのテストは真であり、ゼロの終了コードを返します。次に、&&
リスト内の次のコマンドが実行されます。
次のテスト コマンドは、が存在/data/sb3??????sh25t.sqfs
しないかどうかを確認します。そのファイルが存在しない場合、テスト コマンドはゼロの終了コードを返し、次の&&
リスト オペレーターが次のテスト コマンドを実行します。最初の 3 つのtest
コマンドが true の場合にのみ、最後のテスト コマンドが実行されます。最後のテスト コマンドも真のステートメントである場合、then句が実行されます。
if
現在の仕組みと、問題が発生した理由をご理解いただければ幸いです。
悲しいことに、もう 1 つ問題が発生しています。それは、Unix シェルがどのように機能し、グロビングがコマンド ラインとどのように相互作用するかです。
他のオペレーティング システムの対応するものとは異なり (咳Windows!咳)、Unix シェルは非常にインテリジェントであり、非常に面倒です。
コマンドラインから次のコマンドを試してください。
$ echo "Print an *" #Use quotes
Print an *
これを試してください:
$ echo Print an * #No quotes
Print an bin foo bar...
これにより、すべての「Print an」に続いて、現在のディレクトリ内のすべてのファイルが出力されます。
これを試して:
$ touch foo.out foo.txt
$ echo "Do I have any foo.??? files"
Do I have any foo.??? files
$ echo Do I have any foo.??? files
Do I have any foo.out foo.txt files
繰り返しますが、引用符がなければ、印刷するように依頼したものが変更されました. これは、Unix シェルがおせっかいだからです。何が起こっているかというと、Unix シェルがグロビングパターンを認識し、コマンドが何かをエコーする機会を得る前に、foo.???
そのパターンをすべての一致するファイルに置き換えます。echo
これが次のように発生していることを確認できます。
$ set -x #Turns on `xtrace`
$ touch foo.out foo.txt
+ touch foo.out foo.txt
$ echo "Do I have any foo.??? files"
+ echo "Do I have any foo.??? files"
Do I have any foo.??? files
$ echo Do I have any foo.??? files
+ echo Do I have any foo.out foo.txt files
Do I have any foo.out foo.txt files
$ set +x #Turns off `xtrace`
この機能は、シェルがコマンドをいじった後xtrace
に実行するコマンドを示します。で始まる行は、コマンドが実際に実行する内容を示しています。+
これを取り上げる理由は、考えられる問題によるものです。パターンに一致するファイルが複数ある場合は、パターンに一致するすべてのファイルがテスト コマンドに置き換えられます。もう一度コマンドを見てみましょう。
if [ -f /data/sb3???sh25t.sqfs ] && [ ! -f /data/sb3??????sh25t.sqfs ] && \
[ ! -f /data/sb3????????sh25t.sqfs ] && [ ! -f /data/sb3???????sh25t.sqfs ]
ファイル/data/sb3aaash25t.sqfs
と という名前のファイルがある場合/data/sb3bbbsh25t.sqfs
、if ステートメントの最初のテスト コマンドは実際には次のようになります。
if [-f /data/sb3aaash25t.sqfs /data/sb3bbbsh25t.sqfs ]
-f
パラメータは単一のファイル名しかとらないため、これは機能しない可能性があります。したがって、上記のすべてを行ったとしても、動作しないプログラムになる可能性があります。さらに悪いことに、断続的に失敗し、デバッグがさらに難しくなります。
これを回避する1つの方法は、 test コマンドを忘れて、ls
この回答の一番上の部分で見たようなコマンドを使用することです:
if ls /data/sb3???25t.sqfs 2>&1 > /dev/null
then
echo "At least one file that matches pattern sb3???25t.sqfs exists"
fi
したがって、これを行うことができます:
if ls /data/sb3???sh25t.sqfs 2>&1 > /dev/null && \
! ls /data/sb3??????sh25t.sqfs 2>&1 > /dev/null && \
! ls /data/sb3????????sh25t.sqfs 2>&1 > /dev/null && \
! ls /data/sb3???????sh25t.sqfs 2>&1 > /dev/null
then
はls
複数のファイルを取ることができるためです。
説明が長くなってしまい申し訳ありません。99% の確率で、if
ステートメントがどのように機能するか、[...]
実際には とは完全に別の Unix コマンドであることif
、または厄介な Unix シェルがどのようにつまずくかを理解する必要なく、思い通りに進めることができます。
たまたま、この質問で大当たりしただけです。今日、台本を書く代わりに宝くじをプレイしていたら、上司に上司のことを本当にどう思っているかを伝え、仕事を終えて、文化が非常に原始的な熱帯の島の楽園に引っ越すことができたはずです。エミュレータ。
代わりに、明日塩鉱山に戻ります。
1はい、はい。[
これは BASH シェルに組み込まれており、コマンドではないことはわかってい/bin/[
ます。ただし、組み込みのシェルはコマンド[
のように機能し/bin/[
ます。