それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bashの似てて紛らわしいもの [[ / [ / test  はどこが違うの?

似てて紛らわしいものシリーズ  [[ / [ / test

bash の記号で初心者泣かせの、似てて紛らわしかったり、使い分けがわからなかったり、読み方を間違えてパニックになる記号について書く

[ / [[ の違い

結論から言います [[ / [ は同じものです。 [[ が新しい書き方で機能強化されています。

[ / test の違い

違いはありません。同じものです。*1

[[ は [ とほぼ同じ機能と目的を持ちます。

個人的な意見ですが、 bashを書く上においては [[ でいいと思います。

というか bash が動かない環境なんてもうなくなったんだし、 [[ でいいと思います。個人的な意見ですが。

[/ [[ のヘルプを見てみます。

ちなみに bash で「返す」といえば終了ステータスのことです。

help [[
[[ ... ]]: [[ expression ]]
    条件式のコマンドを実行します。

    条件式 EXPRESSION の評価結果に基づいて 0 または 1 を返します。
     (略
    終了ステータス:
    EXPRESSION の値に基づいて 0 または 1 を返します。
help [
[: [ arg... ]
    条件式を評価します。

[POSIXの古い書き方*2UNIXの考古学をする人なんかが好きそうです。

機能面でも違う

おさらいです。

  • [ は古い
  • [[ は出来ることが増えた

bash を使う限りにおいて 積極的に [[ を使って構いません。っていうか [[ つかえ*3

[ は古いが役に立つ。

[ 後方互換性(つまり古いシェルスクリプトや sh だけに書かれたもの)を実行するときに今でも使われるようです。

dash / sh などのインタプリタや、POSIX互換原理主義に染まってる方々は使うことが多いようです。

なんで [[ なんてものが導入されたのか

TODO:このへんもう少し詳細に書くべき、いつから、なんのために、どうして、どこで使えるのか

便利だから。

[[ は [ に比べてどの辺が便利?

[[ は && が使える
[[ は || が使える
[[ は -e FILEが使える
[[ は -eq が使える
[[ は = でglob マッチが使える
[[ は =~ の 正規表現マッチが使える
[[ は < で文字列長さ比較ができる

[[[ で出来ることは全てできるのです。*4

[ には出来なかった事が[[にはできるのです。

なぜ [ では &&が出来ないのか?

bash のコマンド記号に解釈されてしまうから。

たとえば、<記号は リダイレクトとかぶります。

command  < finename

たとえば && 記号は コマンドの並列実行とかぶります

command && command

たとえば || 記号は パイプライン処理やコマンドの並列記述とかぶります

command || command

たとえば & 記号は バックグラウンド起動とかぶります

command &

これらはすべて、比較処理で衝突を引き起こします。

[ varname  < varname ]            # STDINリダイレクトに見える
[ varname  > varname ]            # STDOUTリダイレクト に見える
[ command || command ]        # パイプに見える
[ command &  command ]       # バックグラウンドに見える

[ を使うとエスケープ処理が必須!?

&& < >| といった記号がシェル・コマンドでで使われます。

そのため、[ を使うとエスケープ処理が必要になっていました。

## リダイレクトに解釈させない
[ varname  \< varname ]            
[ varname  \> varname ]            

エスケープ処理がとってもイメージしにくいですね!!!!

この<めんどくさい>エスケープ処理から私達を開放してくれる。

エスケープの解放軍は我らが [[ なのです。

エスケープ処理の有無による違いの例
$ [ aaaaa \< aaa ]; echo $?
$ [[ aaaaa < aaa ]]; echo $?

どちらのほうが読みやすいですか。少なくとも初心者には後者だと思います。

ね? [[ でいいでしょ?

存在を認知して使い分けできたら、 test/[ なんて捨てちゃえよ。POSIX互換なシェルスクリプトを書くときだけ[[[ に書き換えたらいいでしょ *5

[ / test の更に困ったこと。

[ には2種類あります。

一つが bash のビルトイン関数 [ 。もう一つが /usr/bin/[ です。

[ はそのファイル名のコマンドでもあります。

2種類の [、1つだけの [[

つまり、 [ は二種類あります。次の2つです。

  • function [
  • /usr/bin/[

それに対し [[ は関数のみです

  • builtin function [[

関数とコマンドどちらが優先されるのでしょうか?

優先度は ビルトイン>ファイル

bashにおける優先度は次のようになります。*6

関数
PATH

そのため通常はビルトイン関数が優先度が高いので ビルトイン関数が使われます。

GNU Bashをメインで使う私達にとって、/usr/bin/[ は互換性のためにあると行っても過言ではないと思います。

[test はほぼ同じものです

[ / test は次のすべてが候補になります。

  • ビルトイン関数 [
  • /usr/bin/[
  • /usr/bin/test
  • ビルトイン関数 test

4つもある・・・互換性のために test / [ がいくつもあって更に混乱を招くことになっているのです。

わけわからんわ。 わたしは [[ でいく!みなさんもどうですか?

わたしは、test は help test で比較条件を見るときだけ使うことにしました。

騙されたと思って [[ だけで書くといいですよ。

[[ を使うときの 一つだけ注意

[[ を使うときは test -a / test -o は使わないこと。これでスッキリしますよ

takuya@~$ [[ 0 = 0 && 1 == 1 ]] ; echo $?
0
takuya@~$ [[ 0 = 0 -a 1 == 1 ]] ; echo $?
-bash: 条件式に構文エラーがあります
-bash: `-a' 周辺に構文エラーがあります
takuya@~$

*1: 強いて言うなら [ がペアとなる ] を最後に必要として、test はいらないところ。

*2: [[ もPOSIX予約語ではあるんだけどね 。

*3:意見には個人差が有ります

*4: -a -o は出来ないけど && || がその代わりに使えるから問題なし。

*5:意見には個人差があります

*6: 正確には alias - hash - function - PATH の順番だったと思います