Edited at

[Bash] testと[と[[

More than 1 year has passed since last update.

bashには色々とif文の書き方があるようです。

知らなかったので、まとめてみたいと思います。


事の発端


この調査を始めるきっかけは、Google Shell Style Guideと呼ばれる資料に目を通してからです。

これは「Googleは社内でBashをこう書いてるよ!!」と示したコーディング規約です。

他にも多種多様な言語に対するスタイル集もあります。

C++とかPythonとかもあったりします。

※日本語訳して下さっている私に比べて遥かに優秀な方がいらっしゃいました。

C++

Python

私は綺麗であれば何でもいいと思うので、このGoogle Style Guideに則って書くことが多いです。

※いちいち考えて、その挙句に周りからチクチクされるの面倒ですし、

なにより「Googleがやってますよ!」って言えばなんか説得力ありますよね(無いよ)


Google Shell Style Guide


このGoogle Shell Style Guideには、このような記述があります。

※以下、超テキトーな和訳なので、ご自身で和訳してください。


test と [ それから [[

[[ ... ]] の書式は、testコマンドや/usr/bin/[よりも推奨される。

[[...]]はエラーを減らすことができる。

[[と]]との間でパス名展開や単語分割が行われないし、[[...]]の書式は正規表現を許容するからね


akinomyogaさんのコメントより追記

単語分割については、akinomyogaさんがコメントに補足コードを載せてくださいました。

akinomyogaさんの補足は必見ですよ!(他人任せ)

tadsanさんから頂いたコメントのサイトを見て追記

Googleの記述はregular expressionとなっていたので、正規表現と訳しましたが、


パターンの表記はglobという類のもので、正規表現ではない。


とありますので、正しくはglobライブラリを許容するという意味合いと思われます。




testはコマンド


……うん、そうだろうね。

これはbashの記事だけど、fishさんはtestコマンドが推奨されていますね。

気を付けたいものです。


akinomyogaさんのコメントより

typeコマンドを使えば、ビルトインコマンドも存在することが分かる旨のご指摘をいただきました。



[もコマンド


マジですか。知らなかった

なんか、/usr/bin/[ってシュール……(´・_・`)

$ which [

/usr/bin/[


akinomyogaさんのコメントより

typeコマンドを使えば、ビルトインコマンドも存在することが分かる旨のご指摘をいただきました。


こいつは特殊な比較演算子を要求します。

-ltとか-gtとか-eqとかなんやねんって感じですね。

readonly sample=1

if [ ${sample} -lt 2 ]; then
echo '変数「sample」は2よりも小さい!'
else
echo '変数「sample」は2以上!'
fi



  • -lt -> lesser than


  • -gt -> greater than


  • -eq -> equal

でも仕方ないですよね。

-gtとかは[コマンドのコマンドオプションなんだもん!

ls -l-lと同じだよ!


[[はコマンドじゃない!


$ which [[

$ #何も出力されない

[[はbuilt-in commandと呼ばれるものらしいです。

[[...]]閉じるまで含めてが複合コマンドと呼ばれるものです。

bashそのものに含まれている構文みたいなやつ。

readonly sample=1

if [[ ${sample} < 2 ]]; then
echo 'sampleは2より小さい!'
else
echo 'sampleは2以上!'
fi

まるで高級言語を扱っているようだ……


akinomyogaさんのコメントより

算術式を用いない場合に正しく演算されないパターンがあることをご指摘いただきました。

ご参照下さい。(お前、もっとなんか書け)

ちゃんとテストしてれば分かったこと……いや、辞書式比較という真相まではたどり着けなかったでしょうね……



どれがいいの?


bashがあっても、test[のコマンドが無ければそのシェルスクリプトは使えない。

ところがぎっちょん[[に関しては、bashに組み込まれているからbash環境ならどこでも使える。

どっちがいいかは分かるね?

という感じなのがGoogleの見解らしい。

tadsanさんから頂いたコメントの下記サイトを見て追記


幅広いシェル環境で動作するシェルスクリプトを記述するためにはtestコマンドで「=」を用いるのがよい


[[...]]がbash「のみ」が解釈できる複合コマンドである場合、


  • bashが存在すれば必ず解釈される・できる(メリット)

  • 他のシェルでは必ずしも解釈されるとは限らない(デメリット)

他のシェルを含めてサポートしたい場合は、bash特有の記法は問題になります。

また、Googleの立ち位置は、Shell Style Guideにはこう書いてあります。


Bash is the only shell scripting language permitted for executables.

Restricting all executable shell scripts to bash gives us a consistent shell language that's installed on all our machines.

【テキトー訳】

Bashは(Google社内で)実行できる実行ファイルとして許可されている唯一のシェルスクリプトである。

実行できるシェルスクリプトをBashのみに制限することで、私たちはすべてのマシンで一貫したシェル言語を得られる。


Googleは社内で使用できるシェルスクリプトについてBash以外を認めていないようです。

社内のスクリプト言語をBashに限定する代わりに、統一した思想、コーディングができる。

だから、このスタイルガイドはBashのみに焦点を当てたモノになっているし、

Bash以外はサポートしなくていいから、色んなメリット(※1)のある複合コマンドの方を使おうね

※1:パス名展開や単語分割、正規表現(globライブラリ)を使用できること

ということなのだと思います。

あまり英語が得意ではないので、誤訳があればご指摘ください。

miyuさんから頂いたコメントでより正確な内容が分かります。そちらも参照してください。


最後に、それホント?


Googleの言うことは論理的正しいというか、まぁ間違ってないと思います。

個人的にはC++などの言語と同じ比較演算子が使えるのは精神衛生上よろしかったです。

※全部が全部使えるわけではなさそうだけど(´・_・`)

でも、testや[がないようなbashってあるんでしょうかね?

自宅のubuntu16.04さんのbashrcを見てみました。


.bashrc

(前略)

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
(以下略)

デフォルトがめちゃくちゃ if [ xxx ] 使っとるやんけ!!


akinomyogaさんのコメントより

testや[には通常コマンドのほかに、ビルトインコマンドが存在することをご指摘いただきました。

そのため、test[通常コマンドがなかったとしても使えます。