bashで始めるシェルスクリプト基礎の基礎:Windowsユーザーに教えるLinuxの常識(8)(2/2 ページ)
GUIに対するCUIの優位性の1つとして、作業の自動化が挙げられる。普段行う作業を1つのコマンドにまとめたり、複数のファイルに対して同じ処理を繰り返し行ったりといったことが比較的簡単なのだ。WindowsにもWSH(Windows Script Host)が用意されている。しかし、Linuxのbashスクリプトの方が簡単なのだ。
条件式と繰り返しによるスクリプトの制御
条件判断
引数によって実行時にパラメータを与えることができるようになりました。次に欲しくなるのは、条件判断でしょう。つまり、特定の条件が満たされたときだけコマンドを実行するということです。このための構文が「if文」で、
if 条件文 then 実行文 elif 条件文 実行文 else 実行文 fi
という構造で使います。このうち「elif」はなくてもよいですし、好きなだけ繰り返すこともできます。また、「else」は使わない、あるいは1回だけ使えます。
ここで注意が必要なのは、条件が式ではなく文であることです。すなわち、一般的な真偽によって実行するかしないかを決定するのではないのです。では一体何をもって判断するのかというと、実行した文の「終了ステータス」です。
Linuxでは、あらゆる実行ファイルが終了時に自分自身を呼び出したプロセスに対して整数のコードを返します。これが終了ステータスで、普通は正常に終了したときに「0」を、エラーが発生した場合などはそれに応じた数値を返します。そして、この終了ステータスが0であることが、すなわち真ということになります。言い換えれば、条件文が正常に実行を終了すれば、真であるということです。従って、
if コマンドが正常に終了した then 通常の処理 else エラー処理 fi
という言い回しが成立します。
これでは一般的な条件判断を行うのが難しくなります。そこで、testコマンドが用意されました。このコマンドは、続く条件式を評価して、真ならば0を、偽ならば1を終了ステータスとして返します。このままではちょっと分かりにくいので、「[]」として使えるようになっています。
if [ $# -eq 1 ] then echo one. elif [ $# -eq 2 ] then echo two. elif [ $# -eq 3 ] then echo three. else echo many. fi
というシェルスクリプトなら、引数の数に応じた答えを返します。
testコマンドで使える条件式は、manコマンドで調べられます。代表的なところでは、
条件式 | 意味 |
---|---|
s1 = s2 | 文字列s1とs2が等しい |
s1 != s2 | 文字列s1とs2が等しくない |
n1 -eq n2 | 数値n1とn2が等しい |
n1 -ne n2 | 数値n1とn2が等しくない |
-e file | fileが存在する |
-z s1 | s1の長さが0である |
といったものがあります。
すべてのファイルに同じ処理を
シェルスクリプトの中で私が一番重宝しているのは、「forループ」です。CやPascalといったプログラミング言語にもforループは用意されていますが、シェルスクリプトのforループはちょっと違います。CやPascalでは特定回数のループを実行するのに使われますが、シェルスクリプトでは複数のファイルに対して同じ処理を行うためにあるといっていいでしょう。どちらかというと、オブジェクト指向言語でいうイテレータに近い存在です。
構文は、
for 識別子 in リスト do $識別子を使う文 done
です。これで、リストの内容を1つずつ識別子に代入して、それぞれについてdoとdoneで囲まれた部分を実行します。
簡単な例で試してみましょう。以下の内容のスクリプトをgreeting2.shとして作成します。
#!/bin/sh echo hi $* for name in $* do echo hi $name done
これに3つの引数を与えて実行すると、
$ ./greeting2.sh tom joe mike hi tom joe mike hi tom hi joe hi mike
となります。単純な$*とforループの違いが分かるでしょうか?
実際に私が使っているスクリプトに、
#!/bin/sh echo "<HTML>" echo "<HEAD>" echo "<TITLE></TITLE>" echo "</HEAD>" echo "<BODY>" echo "<H1></H1>" echo "<P>" echo "</P>" for file in *.jpg do convert -size 20%x20% $file ${file%jpg}png echo \<A HREF="$file"\>\<IMG src=\"${file%jpg}png\" alt=\"$file\" hspace=2\>\</A\> done echo "</BODY>" echo "</HTML>"
というものがあります。カレントディレクトリにあるJPGファイルすべてのサムネイルを作り、サムネイル一覧と元のファイルへのリンクを持ったHTMLファイルを出力します。肝はforループで、ループ内の1行目で縦横を20%に縮小したPNGファイルを作り、これをサムネイルとします。ループ内2行目では「<A HREF="https://atmarkit.itmedia.co.jp/ait/articles/0202/05/scan01.jpg"><IMG SRC="https://image.itmedia.co.jp/ait/articles/0202/05/scan01.png" alt="scan01.jpg" hspace="2"></A>」といった行を作って出力します。これを、JPGファイルの数だけ繰り返すわけです。
特定条件での繰り返し
特定の条件が成立している間、繰り返し処理を行うという場合は、whileかuntilを使います。構文としては、
while 条件文 do 実行文 done
となり、条件文についてはforループと同じ扱いになります。終了ステータスが0なら真、0以外なら偽ということです。この点を除けば、CやPascalのwhileループに似ています。
簡単な例として、PATHに設定されている文字列を分解してみましょう。シェルスクリプトは、
#!/bin/sh path=$PATH: while [ $path ] do echo ${path%%:*} path=${path#*:} done
で、実行例は、
$ echo $PATH /usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin: /usr/X11R6/bin:/home/tom-a/bin:/usr/local/pgsql/bin:/usr/interbase/bin $ ./split.sh /usr/local/bin /bin /sbin /usr/bin /usr/sbin /usr/local/sbin /usr/X11R6/bin /home/tom-a/bin /usr/local/pgsql/bin /usr/interbase/bin
です。
手抜きのススメ
シェルスクリプトは制御構造を持っており、かなり高度な作業が行えます。その分、理解して使うにはちょっと苦労するかもしれません。しかし、最初に苦労してしまえば後でずっと楽ができます。また、分からない機能を無理して使わなくても、繰り返し利用するコマンドをまとめるだけでも、ずいぶん省力化できます。ぜひシェルスクリプトを活用して、楽をしましょう。
Copyright © ITmedia, Inc. All Rights Reserved.