スクリプトの制御
出題は「このスクリプトを実行すると何が出力されるか」という形で来ます。書けるだけでは足りず、読めないと当たりません。似た言語の記憶で書くと必ず外すので、シェル独自の形として覚え直します。
なお、1行目の #! から始まる行をシバンと呼び、どのインタプリタで実行するかを指定します。効くのは実行権限を付けて ./script.sh と直接起動したときだけで、bash script.sh のようにインタプリタを明示した場合はただのコメントとして無視されます。
for は3つの書き方がある
リスト形式が基本です。空白で区切られた要素を左から順に取り出します。
ターミナル
for fruit in apple banana cherry; do
echo "$fruit"
done数値の並びは、外部コマンドの seq か bash のブレース展開で作ります。
ターミナル
for n in $(seq 1 3); do echo "$n"; done
for n in {1..3}; do echo "$n"; done
for n in {0..10..2}; do echo "$n"; done # 2 ずつ。bash 4 以降ブレース展開はシェルの機能なので外部コマンドを呼びませんが、{1..$N} のように変数を書くと展開されません。変数で回数を決めるなら seq か C 言語形式を使います。
ターミナル
for ((n = 0; n < 3; n++)); do
echo "$n"
done初期化と継続条件と更新をセミコロンで区切ります。この形は bash の拡張で sh では動きません。for arg in "$@" と書けば、スクリプトに渡された引数を順に処理できます。
while と until は条件が裏返し
while は条件が真である間、until は条件が真になるまで繰り返します。条件を反転させれば同じ動きになります。
ターミナル
n=1
while [ "$n" -le 3 ]; do
echo "$n"
n=$((n + 1))
done$((...)) は算術展開で、中では変数に $ を付けなくても構いません。ファイルを1行ずつ処理する while read も定番です。
ターミナル
while read line; do
echo "[$line]"
done < /etc/hostnameread は標準入力から1行読んで変数に入れ、入力が尽きると 0 以外を返すのでループが止まります。break はループを抜け、continue は残りをとばして次の繰り返しへ進みます。break 2 のように数字を付けると、その数だけ外側のループまで抜けます。
case の閉じは esac、分岐の終わりは ;;
ターミナル
case "$mode" in
start)
echo starting
;;
stop|halt)
echo stopping
;;
*)
echo usage
;;
esacパターンの後ろは閉じ括弧だけで、開き括弧は要りません。各分岐の終わりは ;; で、セミコロン1つでは足りません。複数のパターンは縦棒で並べます。*) は何にも一致しなかったときの既定の分岐で、最後に置きます。全体を閉じるのは esac です。
パターンにはワイルドカードが使えます。C 言語の switch と違い、一致した分岐を実行したらそこで case を抜けるので break は不要です。
引数は特殊変数で受け取る
| 変数 | 意味 |
|---|---|
$0 | スクリプト自身の名前 |
$1 $2 … | 1番目、2番目の引数。10 番目以降は ${10} |
$# | 引数の個数。$0 は数に入らない |
$@ | 全引数。"$@" は1つずつ別の語として展開される |
$* | 全引数。"$*" は全部つないで1つの語になる |
$? | 直前のコマンドの終了ステータス。0 が成功 |
$$ | 実行中のシェルのプロセス ID |
shift は位置パラメータを1つ前へずらします。$2 が $1 になり、$# が1減ります。exit はスクリプトを終了し、exit 0 が成功、それ以外が失敗の合図です。
試験で狙われるところ
case の閉じは esac、if は fi、ループは done です。case の各分岐は ;; で終えます。C 言語形式の for ((...)) は bash 拡張で #!/bin/sh では動きません。$# に $0 は含まれません。そして "$@" と "$*" は展開のされ方が違います。