testまたは[または[[はbashシェル間および他のシェル間でより移植性がありますか?


44

できると思う

$ [ -w /home/durrantm ] && echo "writable"
writable

または

$ test -w /home/durrantm && echo "writable"
writable

または

$ [[ -w /home/durrantm ]] && echo "writable"
writable

3番目の構文を使用するのが好きです。それらはすべての点で、すべてのネガティブおよびエッジケースで同等ですか?移植性に違いはありますか?たとえば、UbuntuのbashとOS Xまたは古い/新しいbashバージョンの場合、たとえば4.0より前/後で、どちらも式を同じように展開しますか?


2
以下のために[ … ][[ … ]]test …、でより完全な答えがある。この大部分は重複質問が
ジル「SO-悪であるのをやめる」14年


1
「移植可能なコードはなく、移植されたコードだけがあります」という格言があります。これに関する私のアドバイス:最も読みやすい形式(おそらく[[...]])を使用し、サポートしたいすべてのプラットフォームで試してください。スクリプトをわかりにくくすることはあまりないので、スクリプトは、あなたもターゲットの視聴者も使用しない古代のプラットフォームで実行されます。それはあなたのコードを読みにくくし、不必要なバグやおそらくセキュリティ問題さえも導入します(opensslのように)。
stefan.schwetschke

回答:


31

[testコマンドの同義語であり、同時にbash組み込みコマンドおよび個別のコマンドです。ただし[[bashキーワードであり、一部のバージョンでのみ機能します。したがって、移植性の理由から、単一[]またはtest

[ -w "/home/durrantm" ] && echo "writable"

2
[POSIXビルトインまたはBashビルトインは何ですか?
サンドバーグ

@Sandburg POSIXは標準であるため、組み込みはできません。インタプリタに何かを組み込むかどうかを定義するのではなく、何をする必要があるのか​​を定義します。これらの規則は、両方の形態に適用され、testそして[同様。シェルがそれ自体で評価を実行できる場合、プロセスが節約され、多少速くなりますが、結果は同じでなければなりません。
バクサウ

@Bachsauは、まだ動作が[POSIXによって定義されているかどうかに関するSandburgの質問に答えていないため、最大限に移植性があります(私が間違っていなければ、この質問の全体のポイントと思われます)
JamesTheAwesomeDude

@Sandburg(この全体でつまずき誰に):はい、それは両方のように見える[test POSIXですどちらも「推奨」されていないように見えますが、それらの間の唯一の違い(?)は、test「オプションの終わりを示す区切り文字として「-」引数を認識しないことです」(?- - 」されるため、エンド・オブ・引数として認識され[、私は実際にとにかくのために良いテストケースを思い付くことができない、あなたはIMO、だろう。しかし、私脱線使用。。。[エレガントに見えますが、test)isclearlyコマンド
JamesTheAwesomeDude

35

はい、違いがあります。最もポータブルなのはtestまたは[ ]です。これらは両方ともPOSIX test仕様の一部です。

if ... fi構築物はまた、されるPOSIXで定義され、完全に移植する必要があります。

[[ ]]あるkshのいくつかのバージョンでも存在している機能bashすべての近代的なもの)、でzsh、おそらく他の人ではなく、中には存在しないshか、dashまたは他のさまざまな単純なシェル。

したがって、スクリプトを移植可能にするには[ ]testまたはを使用しますif ... fi


10
ただ、ノートに、bashそしてzsh支えてきた[[非常に長い時間(bash90年代後半、それを追加zsh遅くとも2000よりも、それがあれば、私は驚かれると思い、これまであなたがすることなく、いずれかのバージョンに遭遇する可能性は低いので、サポートを欠いていました)[[。異なるPOSIX準拠のシェル(などdash)に遭遇する可能性がはるかに高くなります。
chepner

1
の興味深い機能の1つ[[は、パラメーター展開を引用符で囲む必要がないことです。[[-f $file]]イベント$fileは、空白文字が含まれている場合に機能します。
ヘルパーメソッド14年

2
@helpermethodも、組み込みの正規表現[[ $a =~ ^reg.*exp.*$' ]]
GnP 14年

20

それ[] && cmdif .. fi建設と同じではないことに注意してください。

ときどきその動作はかなり似ており、の[] && cmd代わりに使用できますif .. fi。しかし、たまにしか。if条件がある場合に実行するコマンドが複数ある場合、またはif .. else .. fiロジックに注意を払う必要がある場合。

いくつかの例:

[ -z "$VAR" ] && ls file || echo wiiii

とは異なります

if [ -z $VAR ] ; then
  ls file
else
  echo wiii
fi

lsが失敗した場合、echo実行されるため、では発生しませんif

もう一つの例:

[ -z "$VAR" ] && ls file && echo wiii

とは異なります

if [ -z "$VAR" ] ; then
   ls file
   echo $wiii
fi

この構造は同じように機能しますが

[ -z "$VAR" ] && { ls file ; echo wiii ; }

;エコーが重要であり、エコーが存在する必要があることに注意してください。

したがって、上記のステートメントを再開すると言うことができます

[] && cmd ==最初のコマンドが成功した場合、次のコマンドを実行します

if .. fi == if条件(テストコマンドでもある可能性があります)の場合、コマンドを実行します

間の移植のためにそう[して[[の使用[のみ。

ifPOSIX互換です。ですから、の間で選択する必要がある場合[if、あなたのタスクと予想される動作を見て選択します。


私はおそらくただ密集していますが、違いがどうなるか見ていません...これらの2つの構成が異なる結果を与える例を挙げていただけますか?
evilsoup

1
@evilsoup、更新。私は最高の説明者ではないかもしれませんが、それが今明らかになることを願っています。
ラッシュ14年

1
非常に良い点、急いで。最初の例の安全な形式は次のようになります[ -z "$VAR" ] && { ls file; true; } || echo wiiii。これはもう少し冗長ですが、if...fi構造よりもまだ短いです。
PM 2リング14年

質問を[vs [[vs testとし、if ... fi vs &&についてではなく1つの質問にします。残念ながらこれを行うと、この答えはオフポイントのように見えます。これにつながった最初の質問に焦点を当てていないことをおologiesびします。生き、学びましょう。:)
マイケルデュラント14年

a)これは主に良い答えですが、別の質問に対する答えだからです。b)あなたは提示[if、代替物として、しかし彼らはそうではありません。実際に&&それがを置き換えていifます。[多くのように、いくつかのコードを実行し、ステータスを返すlsgrepなります。ififの後に指定されたコマンド(ステートメント)の戻り状況に応じて実行を分岐します。コマンド(ステートメント)の場合もあります。&&前のステートメントが0を返した場合にのみ、次のステートメントを実行しif..then..fiます。
GnP 14年

9

それは実際にあります&&交換されることif、ありませんtestifコマンドは「成功した」(ゼロ)終了ステータスを返したかどうかシェルスクリプトのテストで声明。あなたの例では、コマンドは[です。

そのため、実際には、テストを実行するために使用されるコマンドと、テストの結果に基づいてコードを実行するために使用される構文という2つの変更があります。

テストコマンド:

  • test標準化され、コマンド文字列とファイルの特性を評価するため、あなたの例では、コマンドを実行していますtest -w /home/durrantm
  • [等しく標準化されたコマンドのエイリアスであり]、括弧で囲まれた式のように見えるために必須の最後の引数を持ちます。だまされてはいけません、それはまだ単なるコマンドです(あなたのシステムにが呼ばれるファイルがあることに気付くかもしれません/bin/[
  • [[いくつかのシェルに組み込まれているtestコマンドの拡張バージョンですが、同じPOSIX標準の一部ではありません。ここで使用していない追加オプションが含まれています

条件式:

  • &&(オペレータここで規格化は)どちらも0を返した場合(真表す)の2つのコマンドを評価し、0を返すことによって、論理AND演算を行います。最初のコマンドがゼロを返した場合にのみ2番目のコマンドを評価するため、単純な条件として使用できます
  • if ... then ... fi(構築ここで規格化は)「真実の」判定と同様の方法を使用するが、内のステートメントの化合物のリストを可能thenむしろによって得られる単一のコマンドよりも、句&&短絡、及び提供elif及びelse書き込みにくい句を、とのみ&&を使用し||ます。ステートメントの条件を囲む括弧はないことに注意してifください

したがって、以下はすべて同じように移植可能で、完全に同等のサンプルのレンダリングです。

  • test -w /home/durrantm && echo "writable"
  • [ -w /home/durrantm ] && echo "writable"
  • if test -w /home/durrantm; then echo "writable"; fi
  • if [ -w /home/durrantm ]; then echo "writable"; fi

以下も同等ですが、非標準的な性質のために移植性が低くなり[[ます:

  • [[ -w /home/durrantm ]] && echo "writable"
  • if [[ -w /home/durrantm ]]; then echo "writable"; fi

はい、if .... fiの部分を削除して、これを1つの質問にしました。
マイケルデュラント14年

7

移植性のために、test/を使用します[。ただし、自分自身やスクリプトを読んでいる他の人の健全性のために、移植性が必要ない場合はを使用します[[。:)

BashFAQも参照What is the difference between test, [ and [[ ?してください


7

Bourneのような世界の外部への移植性が必要な場合:

test -w /home/durrantm && echo writable

最もポータブルです。Bourneのシェルcshおよびrcファミリーで動作します。

test -w /home/durrantm && echo "writable"

出力希望"writable"の代わりwritableのシェルでrc家族(rcesakanga"特別なものではありませんが)。

[ -w /home/durrantm ] && echo writable

コマンドを持たないシステムcshまたはそのrcファミリのシェルでは機能しません(一部はエイリアスを持っていることがわかっていますが、エイリアスは持っていません)。[$PATHtest[

if [ -w /home/durrantm ]; then echo writabe; fi

Bourneファミリーのシェルでのみ動作します。

[[ -w /home/durrantm ]] && echo writable

のみで動作しますksh(それが元の場所)、zshおよびbash(ボーン・ファミリのすべての3)。

fish必要なシェルではどれも動作しません:

[ -w /home/durrantm ]; and echo writable

または:

if [ -w /home/durrantm ]; echo writable; end

0

どちらかを選択するための私の最も重要な理由if foo; then bar; fifoo && bar全体のコマンドの終了ステータスが重要であるかどうかです。

比較する:

#!/bin/sh
set -e
foo && bar
do_baz

で:

#!/bin/sh
set -e
if foo; then bar; fi
do_baz

あなたは彼らが同じことをしていると思うかもしれません。ただし、foo失敗した場合(または、視点に応じてfalse)、スクリプトが終了したため、最初の例ではdo_bazは実行されませんset -e。次のようなことをしている場合に非常に便利です。

cd /some/directory
rm -rf *

cd何らかの理由で失敗した場合、スクリプトを実行し続けたくないでしょう。


1
fooどちらの場合でも、失敗してもスクリプトは中止されません。すなわち、のための特別なケースのset -e一部の左に(コマンド条件として評価された場合(&& / ||又はにおけるIF /つつ/)/ ELSIF ...条件までA失敗した。bar両方の場合において、シェルを出ます。
ステファンシャゼル14年

2
希望するcd /some/directory && rm -rf -- *cd /some/directory || exit; rm -rf -- *(まだ隠しファイルを削除しません)。個人的には、set -e正しいコードを書く努力をしない言い訳として使用するという考えは好きではありません。
ステファンシャゼル14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.