前回はコマンドのシーケンスが「リスト」と呼ばれていることを説明した。リストはさらに演算子やコンストラクタによってより複雑な構造を取ることがある。今回はこの辺りについて解説しよう。プログラミング言語の世界では、こうした機能は「制御構文」と呼ばれることが多い。

ショートサーキットリスト演算子

シェルでは「&&」または「||」というリストオペレータを利用することができる。基本的に次のような使い方をする。

■ANDリスト演算子

コマンド1 && コマンド2

■ORリスト演算子

コマンド1 || コマンド2

&&は、左側のコマンドの終了ステータスが0だった場合に右側のコマンドを実行し、0以外だった場合には実行しない。||は&&の逆で、左側のコマンドの終了ステータスが0以外だった場合に右側位のコマンドを実行し、0だった場合には実行しない。

短い書き方のif制御構文のような使い方ができる書き方で、比較的よく利用するので覚えておきたい。

フロー制御コンストラクタ if

リストの終了ステータスによって処理を切り分けるには「if」を利用する。ifの使い方は次のようになる。

■ifの使い方

if リスト1
then
    リスト2
fi

■if-elseの使い方

if リスト1
then
    リスト2
else
    リスト3
fi

■if-elif-elseの使い方

if リスト1
then
    リスト2
elif リスト3
then
    リスト4
else
    リスト5
fi

ifの後のリストの終了ステータスが0だった場合、「then」の後のリストが実行される。ifの後のリストの終了ステータスが0以外だった場合、「elif」または「else」以降へと処理が移っていく。

elifはifと同じだ。elifのあとのリストの終了ステータスが0だった場合、thenの後のリストが実行されるし、そうでなければさらに次のelseやelifへ処理が移っていく。

ifやelifのリストが全て0以外で終了してthenやelseの後のリストが1つも実行されなかった場合のifとしての終了ステータスは、0として扱われる。

フロー制御コンストラクタ「case」

ifのように処理を分岐させるコンストラクタに「case」がある。caseの使い方は次のようになる。

■caseの使い方

case ワード in
パターン1)
    リスト1
    ;;
パターン2)
    リスト2
    ;;
...
esac

caseはワードによって処理を分けるフロー制御コンストラクタで、パターンに一致した場所のリストが実行される。;;が使われている場合、そこでリストの実行は終了となる。;;ではなく「;&」が使われている場合には、そこで処理は終了せずにさらに次のリストへ処理が続いていく。

フロー制御コンストラクタ「while」「until」

繰り返し処理を行う場合には「while」が使われる。whileの使い方は次のようになる。

while リスト1
do
    リスト2
done

whileの後のリストの終了ステータスが0の場合、「do」と「done」の間のリストを実行する。似たようなフロー制御コンストラクタに「until」がある。untilの使い方は次のようになる。

until リスト1
do
    リスト2
done

untilはwhileとは条件が逆になる。untilの後のリストの終了ステータスが0以外の場合に、doとdoneの間のリストを実行する。終了ステータスは、最後に実行したリストの終了ステータスとなる。

フロー制御コンストラクタ「for」

繰り返し処理を行うコンストラクタにはもう1つ「for」がある。forの使い方は次の通りだ。

for 変数 in ワード1 ワード2 ...
do
    リスト
done

変数に「ワード1」が代入された状態でdoとdoneの間のリストが実行される。処理が終わると、次は変数に「ワード2」が代入され、先ほどと同じ処理が行われる。これが指定されたワードが終了するまで繰り返される。

forの記述では、次のように「in」の指定を省略することもできる。

for 変数
do
    リスト
done

ifの指定を省略した場合、実際には次のようにinが指定されたものとして処理が行われる。

for 変数 in "$@"
do
    リスト
done

breakとcontinue

繰り返し処理は「break」または「continue」を使ってジャンプ処理させることができる。breakとcontinueの使い方は次の通りだ。

■breakの使い方

break

■continueの使い方

continue

breakを実行すると、処理中のループを抜け、そのループ処理は終了となる。continueを実行すると、そのループの最後まで処理が飛び、次のループ処理が実行される。

breakおよびcontinueは次のようにネスト数を指定して実行することもできる。「break」と書くのと「break 1」と書くのは同じことだ。

■ループネスト数を指定したbreak

break ループネスト数

■ループネスト数を指定したcontinue

continue ループネスト数

ループネスト数を指定した場合、その指定数まで外のループまで処理が及ぶ。例えば、whileがネストして2重になっていた場合、「break 2」と書いておけば、外側のループまで抜けることができる。

フロー制御コンストラクタ

シェルが提供しているフロー制御コンストラクタは制御構文として十分であり、ほかのプログラミング言語と同じような書き方を行うことができる。しかし、これはシェルスクリプトに限った話ではないが、フロー制御コンストラクタを使って必要以上に構造を複雑にするのは避けたほうがよい。複雑にしすぎると、可読性が下がるからだ。

前回、グルーピングの方法として「()」を紹介した。その前は、パイプラインを取り上げている。これらはサブシェルで実行されているため、スコープがほかのプログラミング言語とかなり違う。理解しているなら問題ないが、これらを組み合わせて複雑な構造を書いてしまうと、何が何だかよくわからなくなることがある。シンプルな書き方ができるときは、シンプルに書いたほうがよいだろう。

参考資料