理解しておくと便利なシェルの制御構造
bashは単純なコマンドだけではなく制御文も受けつけます。制御構造を使うと定型的な処理を行うのが楽になります。
コマンドの繰り返し
シェルで繰り返しを行うにはfor文、while文、until文などがあります。ここでは、コマンドライン上でよく利用されるfor文、while文を紹介します。
for文
for文ではリストから項目を1つずつ取り出します。取り出した値は、指定した一時変数に格納され、処理内容が実行されます。処理内容はdoとdoneの間に記述します。
for 変数 in リスト do 繰り返し実行される処理内容 done
コマンドライン上では次のように使用します。
$ for i in 1 2 3 4 5 [改行] > do [改行] > echo "$i" [改行] > done [改行] 1 2 3 4 5
上記の「>」は2次プロンプトと呼ばれるもので、コマンドがまだ完結していないときにシェルが表示します。また、次のように1行で書くこともできます。
$ for i in 1 2 3 4 5; do echo "$i"; done
csh・tcshでは、複数行の構造を1行にまとめることはできません。また、bash 3以降では、数字の範囲を次のように指定することも可能です。
$ for i in {1..5}; do echo "$i"; done
なお for文では、次のように条件の設定を指定することもできます。
for((i=0; i<5; i++)); do echo "$i"; done
算術式は必ず二重括弧(())を使ってください。
さらに、doとdoneは中括弧("{"と"}")で置き換えることができるため、次のようにC言語のような形式で指定することも可能です。
for((i=1; i<5; i++)){ echo "$i"; }
次の例ではfor文を利用して、すべてのファイル名に拡張子「.bak」を付けています。
$ for file in *; { mv -i "$file" "$file.bak"; }
なお、スペースなどを含むファイル名を正しく処理させるために、変数は「""」で囲むようにしてください。
while文
while文は繰返し処理の制御を行います。構文は次のようにwhileに続けて条件を記述します。処理内容はdoとdoneの間に記述します。
while 条件 do 繰り返し実行されるコマンド done
次の例は、すべてのファイル中の行数を表示します。while文を利用すると次のように記述できます。
$ ls * ← 全ファイル・ディレクトリを一覧表示
sec1.txt sec2.txt sec3.txt
$ find ./ -type f | while read file; do wc -l "$file"; done
3 ./sec1.txt
4 ./sec2.txt
5 ./sec3.txt
findコマンドの出力結果を、while readでfileという変数に取り込みます。
古いバージョンのfindコマンドを利用している方は、「-print」オプションを追加しないと正しく動作しません。
条件分岐
シェルの条件分岐にはcase文とif文があります。ここではif文を取り上げます
if文
if文は、コマンドの終了コードに基づき、行わせる処理が異なる場合に使用します。
構文は次のように、コマンド1の実行結果が成立(終了コードが0の場合)なら「コマンド1が成立した時に実行するコマンド」を実行します。「elif コマンド2...」は省略することも複数記述することもできます。「else ...」は省略可能です。
if コマンド1
then
コマンド1が成立(終了コードが0の場合)した時に実行するコマンド
[elif コマンド2
then
コマンド1が不成立で、コマンド2が成立した時に実行するコマンド]
[else
コマンド1、コマンド2が不成立の時に実行するコマンド]
fi
次の例ではif文を利用して、ディレクトリの中に存在するディレクトリとファイルの数を表示しています。
$ ls * ← 全ファイル・ディレクトリを一覧表示
sec1.txt
dir:
dir2/ sec1.txt sec2.txt sec3.txt
$ for dir in *; { if [ -d "$dir" ] ; then echo "$dir:"; ls "$dir" | wc -l; fi; }
dir:
4
「-d "$dir"」の-dオプションは、引数$dirがディレクトリであるかどうかを調べます。ディレクトリの場合は成功します(0を返します)。リストを利用して、次のようにも記述できます。
$ for dir in *; { [ -d "$dir" ] && (echo "$dir:"; ls "$dir" | wc -l); }
理解しておくと便利な正規表現
UNIXで使われる正規表現には、基本正規表現と拡張正規表現の2種類があります。
sed、grep、exprはデフォルトでは基本正規表現を使用します。またperlやruby、emacsなどは、それぞれ独自に拡張した正規表現を使用しているので注意が必要です。
| 記号 | 意味 |
| [] | 括弧内の任意の1文字にマッチします。 |
| [^ ] | 括弧内以外の文字にマッチします。 |
| ¥(パターン¥) | 正規表現パターンをひとまとめのグループとして扱います。 |
| * | 直前の正規表現の0回以上の繰り返しを行います。 |
| ¥{n,m¥} | 直前の正規表現のn回以上、m回以下の繰り返しを行います。 |
| ¥{n¥} | 直前の正規表現のn回の繰り返しを行います。 |
| ¥{n,¥} | 直前の正規表現のn回以上の繰り返しを行います。 |
| ^ | 文字列の先頭、あるいは行の先頭にマッチします。 |
| $ | 文字列の最後、あるいは行の最後にマッチします。 |
| ¥ | 正規表現に使われる記号を普通の文字として扱います。 |
| . | 任意の1文字にマッチします。 |
ファイル名から拡張子を取り除いて表示するコマンドは、sedコマンドを利用して次のように記述できます。
$ ls test1.bak test2.c test3.html test4.txt $ ls * | sed 's/¥.[^.]*$//' test1 test2 test3 test4
「sed 's/\.[^.]*$//'」とすれば最後のピリオド以降行末までが削除されます。また、入力がピリオドを含まない(拡張子を持たない)場合は、何も行いません。このコマンドであれば、拡張子が不定の場合でも使用可能です。
まとめ
今回は、覚えておくべきシェルの制御構造、正規表現に関して簡単に説明しました。
次回からは、これらのコマンドや制御構造を組み合わせて、業務で楽できるようなテクニックをテーマ別に紹介します。
