シェルスクリプト[[]]のススメ

こんにちは。
最近シェル芸かっこいいなと思い立ちシェルスクリプトの勉強をしています。
下地です。
今日はシェルスクリプトの小技を紹介しようと思います。
宜しくお願いします。

if文お供 [と[[

シェルスクリプトを書くときに良くお世話になるのがif文です。
ifは条件分岐を行う制御構造ですが、一緒に使われるのが[です。
私も最初勘違いしていたのですが、これは条件式ではなくてコマンドです。
別名testコマンドと呼ばれています。

#!/bin/bash
string=aaa
if [ "$string" = aaa ]; then
echo OK
fi

という感じで使われます。
このように条件を判断する[コマンドですが、似たような構文として[[という書き方があります。
これは[とは違い、コマンドではなく構文です。
[を使うときよりシンプルにかける場面が多です。
ではどのような時にメリットがあるのが見ていきましょう。

AND条件やOR条件を纏めて扱う

複数の条件を組合わせてifで分岐させたいとき、&&や||という構文を使います。
例えば以下のような感じです。

#!/bin/bash
x=6
if [ $x -gt 3 ] && [ $x -lt 7 ]; then
echo xは3より大きく7より小さい
else
echo xは3より小さく7より大きい
fi

これを実行すると「xは3より大きく7より小さい」が表示されます。
[を使う場合、&&や||を中に含むことはできません。
例えばifの部分を

if [ $x -gt 3 && $x -lt 7 ]; then

という風に纏めてしまうとエラーになります。
しかし[[を使うと、&&や||を纏めて扱うことができます。

#!/bin/bash
x=6
if [[ $x -gt 3 && $x -lt 7 ]]; then
echo xは3より大きく7より小さい
else
echo xは3より小さく7より大きい
fi

これで動作します。

単語分割しない

例えば以下のスクリプトを実行してみます。

#!/bin/bash
str='abc xyz'
if [ $str = abc ]; then
echo yes
else
echo no
fi

するとエラーが出てしまいます。
これは、strという変数がパラメータ展開された後に、さらに単語分割が行われて
[ abc xyz = abc ]
という風に解釈されてしまい、左辺に2つの単語を指定したことになるからです。
これを解決するには、

if [ "$str" = abc ]; then

という感じで変数名を"で囲みます。
"でクォートしておけば単語分割が行われないため、[の引数で変数を扱う場合は常に"でクォートしておくことが推奨されます。
ここで[[を使ってみます。

#!/bin/bash
str='abc xyz'
if [[ $str = abc ]]; then
echo yes
else
echo no
fi

という感じで[[でくくると正常に処理されます。
[[]]の内側は特別で、通常とは違い以下のような動作をします。
・単語分割が無効になる
・パス名展開が無効になる
・空文字列でも1つの要素として扱われる

パターンマッチを使う

[[]]の内側では*などのパス名展開が無効になります。
ただし、[[]]の中で == 及び = 及び != の右辺にパス名展開の記号を書いた場合はパターン文字列としてみなされます
つまり、この仕様で条件分岐にてワイルドカードを使うことができます。
次の例を見てみます。

#!/bin/bash
string1=abc
if [[ $string1 == a* ]]; then
echo yes
else
echo no
fi

このスクリプトは正しく動作し、結果として「yes」を表示します。
更に右辺に指定するのは文字列だけでなく変数でもOKです。
以下のようにすると同様の動作をします。

#!/bin/bash
string1=xyz
string2='x*'
if [[ $string1 = $string2 ]]; then
echo yes
else
echo no
fi

これも正常です。
一方[ではワイルドカードを扱うことはできません。
以下のような例では正しく動作しません。

#!/bin/bash
str1=xyz
if [ $str1 = x* ]; then
echo yes
else
echo no
fi

拡張正規表現を使う

[[では、==に似た機能として=~という演算子が使用できます。
これは右辺に書いた文字列を拡張正規表現とみなして、左辺がそれにマッチするか判定します。
以下のように使います。

#!/bin/bash
str1=/home/denet
if [[ $str1 =~ ^/home/[^/]+$ ]]; then
echo yes
else
echo no
fi

このスクリプトを実行すると、右辺が正規表現と解釈されるので「yes」が表示されます。

まとめ

bashでは、[[]]は[と違ってコマンドではなく条件を評価するための構文です。
[では表現できないシンプルな書き方ができるので、使ってみると便利です。
お読み頂きありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA