拡張 POSIX シェルスクリプト Advent Calendar 2013、15日目の記事です。 書くのが遅れ、ネタは尽きかけ、 マニアックさやニッチさが足りなくなってきているような気がします。 ふつうの内容ですみません。
今日は test
, [
, [[
コマンドの違いについてざっくり紹介します。
[[
がある bash, ksh, zsh ならば test
と [
の使用は避け、
[[
を使用すべきです。
[
は慎重に使わないと様々な罠にかかるため危険です。
(翌日のネタも併わせてどうぞ)
test
と [
の違い
どちらもシェルの組込みコマンドです。名前が違うのと
[
は最後の引数を ]
にしなければいけない縛りがある以外、
動作まったく同じです。
不定期に「test
は外部コマンド (/usr/bin/test
) で [
は組込みコマンドだ」
などという話が涌きますが、私の不確かな記憶が確かならば(どっちだよ)、
そのようなシェルは見たことがありません。(csh 系は知らん)
現代のシェルの中では busybox の組込み sh がコンパイル時に
test
を組込みにしない実装にすることが可能です。
メモリやストレージの容量が厳しい組込み機器内の実装であれば、
test
が組込まれていない sh も珍しくないかもしれません。
[[
の違い 1: ワード分割とパス名展開がされない
[[ 〜 ]]
内では変数展開後にワード分割されません。
クォートの有無には依りません。
変数にワード区切り文字 ($IFS
) が含まれていても安全です。
zsh 使いであればデフォルトの set +o SH_WORD_SPLIT
状態と等価であると言うと理解し易いかもしれません。
また、[[ 〜 ]]
内ではパス名展開もされません。
クォートやパスのパターンマッチ文字 ?
, *
などの有無には依りません。
zsh のデフォルトの set +o GLOBSUBST
状態と等価です。
次の例のように [ 〜 ]
内ではクォートなしの変数展開は変数値に依って式が変化してしまう可能性があります。
何かしら意図がない限り、変数展開は必ずダブルクォートすべきです
$ line='foobar'
$ [ $line == foobar ]; echo $?
0
$ line='foo bar'
$ [ $line == foobar ]; echo $?
bash: [: 引数が多すぎます
2
$ line='/*'
$ [ $line == foobar ]; echo $?
bash: [: 引数が多すぎます
2
$ line='1 -eq 1 -o xxx'
$ [ $line == foobar ]; echo $?
0
[[
では変数展開をダブルクォートする必要がありません。
$ line='foobar'
$ [[ $line == foobar ]]; echo $?
0
$ line='foo bar'
$ [[ $line == foobar ]]; echo $?
1
$ line='/*'
$ [[ $line == foobar ]]; echo $?
1
$ line='1 -eq 1 -o dummy'
$ [[ $line == foobar ]]; echo $?
1
[[
の違い 2: 数値の比較演算子では左右の値が算術式展開される
-eq
, -ne
などの数値の比較演算子を用いたとき、
比較対象の値を算術式として評価した結果を比較します。
つまり、次の例の [ 〜 ]
と [[ 〜 ]]
は等価です。
$ var=123
$ [ "$((var))" -eq 123 ]; echo $?
0
$ [[ var -eq 123 ]]; echo $?
0
$ varname=var
$ [ "$(($varname))" -eq 123 ]; echo $?
0
$ [[ $varname -eq 123 ]]; echo $?
0
let
コマンドや算術式展開と同様に、単純な値だけでなく式も書けます。
式に空白を含めたい場合はクォートします。
$ let 'x = 2 ** 10'
$ echo $x
1024
$ echo $((2**10))
1024
$ [[ 2**10 -eq 512+512 ]]; echo $?
0
$ [[ '2 ** 10' -eq '512 + 512' ]]; echo $?
0
[[
の違い 3: 文字列の比較演算子 ==
の動作が異なる
[[ 文字列1 == 文字列2 ]]
のように文字列の比較演算子 ==
の右辺がクォートされていない場合、完全一致ではなくパターンマッチとなります。
$ [ /foobar == /fooba[rz] ]; echo $?
1
$ [ /foobar == '/fooba[rz]' ]; echo $?
1
$ [[ /foobar == /fooba[rz] ]]; echo $?
0
$ [[ /foobar == '/fooba[rz]' ]]; echo $?
1
$ [[ /foobar == /foo* ]]; echo $?
0
$ [[ /foobar == '/foo*' ]]; echo $?
1
[[
の違い 4: 文字列の比較演算子の種類が多い
[
にはない 3 つの文字列用比較演算子が用意されています。
[[ 文字列 =~ 正規表現 ]]
- 文字列が正規表現に一致すれば真。
[[ 文字列1 < 文字列2 ]]
- 現在のロケールの辞書順で文字列1が文字列2よりも前なら真。
[[ 文字列1 > 文字列2 ]]
- 現在のロケールの辞書順で文字列1が文字列2よりも後なら真。
<
と >
はリダイレクト用のメタ文字と被っていますが、
クォートしたりエスケープする必要はありません。
[[
の違い 5: 論理演算子が異なる
[[
の論理演算子の論理積 (AND) は &&
、論理和 (OR) は ||
です。
[
の -a
と -o
は使用できません。
次の例の [ 式 -a 式 ]
と [[ 式 && 式 ]]
は等価です。
$ [ -n "$foo" -a -n "$bar" ]; echo $?
…
$ [[ -n $foo && -n $bar ]]; echo $?
…
次の例の [ 式 -o 式 ]
と [[ 式 || 式 ]]
は等価です。
$ [ -n "$foo" -o -n "$bar" ]; echo $?
…
$ [[ -n $foo || -n $bar ]]; echo $?
…
[
のほうは [ -a ファイル ]
、[ -o オプション名 ]
と重複している上に見難いですが、
[[
のほうは [ 式 ] && [ 式 ]
、[ 式 ] || [ 式 ]
記法と同じくらい見易いですね。
ところで、12月25日はクリスマスな上に、
OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね!
っ http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority
逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!
私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → https://www.osstech.co.jp/recruit/
よろしければ、これまで参加した/参加予定のほかの Advent Calendar もどうぞ。
- Ansible Advent Calendar 2023
- シェル芸 Advent Calendar 2023
- 闇の魔術に対する防衛術 Advent Calendar 2023
- Ansible Advent Calendar 2023
- Ansible Advent Calendar 2020
- DNS温泉 Advent Calendar 2019
- OSSTech Advent Calendar 2019
- Ansible Advent Calendar 2018
- OSSTech Advent Calendar 2018
- Debian/Ubuntu Advent Calendar 2017
- Linux Advent Calendar 2017
- Shell Script Advent Calendar 2017
- Shell Script Advent Calendar 2016
- OpenLDAP と仲間たち Advent Calendar 2015
- Postfix Advent Calendar 2014
- 拡張 POSIX シェルスクリプト Advent Calendar 2013