testと条件式
角括弧の内側に空白が無いだけで「コマンドが見つかりません」と言われます。[ が記号ではなくコマンドだからです。ここを飲み込むと、条件式の問題はほとんど落とさなくなります。
[ はコマンドなので、内側に空白が要る
[ は /usr/bin/[ という実行ファイルとして実在します。だから次の2つはまったく同じ意味です。
ターミナル
test -f /etc/hostname
[ -f /etc/hostname ]コマンドである以上、コマンド名と引数は空白で区切ります。閉じる ] も [ に渡す最後の引数なので、その前にも空白が要ります。
ターミナル
[ -f /etc/hostname ] # 正しい
[-f /etc/hostname] # 誤り。[-f というコマンドを探しに行って失敗する
[ -f /etc/hostname] # 誤り。] が閉じられていないと怒られる選択肢を見比べるときは、まず空白の有無を確かめてください。
[[ ]] は bash の拡張構文です。内部で単語分割やパス名展開が起きないため未定義変数に強く、&& || をそのまま書け、=~ で正規表現も使えます。ただし POSIX sh では動かないので、移植性を問われる場面では [ ] を選びます。
-e はディレクトリでも真、-f は通常ファイルだけ
| 演算子 | 真になる条件 |
|---|---|
-e file | 存在する。種類は問わない |
-f file | 存在して、かつ通常ファイル |
-d file | 存在して、かつディレクトリ |
-L file | シンボリックリンクである |
-r -w -x | 読み取り、書き込み、実行の権限がある |
-s file | 存在して、かつサイズが 0 より大きい |
f1 -nt f2 / f1 -ot f2 | f1 のほうが新しい / 古い |
ディレクトリに対して -e は真ですが -f は偽です。空のファイルに対して -f は真ですが -s は偽です。この2組が引っかけの材料になります。
数値はアルファベット、文字列は記号
| 種類 | 演算子 |
|---|---|
| 数値の比較 | -eq 等しい / -ne 等しくない / -lt より小さい / -le 以下 / -gt より大きい / -ge 以上 |
| 文字列の比較 | = 等しい / != 等しくない |
| 文字列の長さ | -z 長さが 0 / -n 長さが 0 より大きい |
数値に = や < を使ってはいけません。[ 10 = 10.0 ] は文字列比較なので偽ですし、[ 5 < 10 ] の < はリダイレクトと解釈されます。逆に文字列へ -eq を使うと数値として解釈できずエラーになります。
変数が空のときに [ $VAR = abc ] と書くと、展開の結果が [ = abc ] となって構文エラーです。[ "$VAR" = abc ] とダブルクォートで囲みます。
真は 0、偽は 0 以外
シェルでは真が 0、偽が 0 以外です。C 言語などとは逆なので混乱しやすいところです。直前のコマンドの終了ステータスは $? で取れます。
ターミナル
[ -d /etc ]
echo $? # ディレクトリなら 0 が出るif は、条件に書いたコマンドの終了ステータスが 0 かどうかだけを見ています。だから if grep -q root /etc/passwd; then のように、[ ] 以外のコマンドも条件に書けます。
ターミナル
mode=start
if [ "$mode" = start ]; then
echo starting
elif [ "$mode" = stop ]; then
echo stopping
else
echo usage
fithen を同じ行に書くなら直前にセミコロンが要ります。閉じるのは fi です。論理の組み合わせは [ ... ] && [ ... ] のように2つ並べてつなぐ書き方が安全で、1つの角括弧の中で使う -a と -o は POSIX で非推奨です。否定は感嘆符です。
試験で狙われるところ
[ の直後と ] の直前の空白。数値は -eq -lt、文字列は = != という取り違え。真が 0 で偽が 0 以外という向き。-e はディレクトリでも真で -f は通常ファイルだけ。そして [[ ]] は bash 専用で /bin/sh のスクリプトでは使えないこと。この5点です。