まずはおさらい、シェル制御構造と正規表現の基礎
この記事は2008年~2009年頃に翔泳社で連載していた「業務で楽するためのUNIXテクニック集」の原本です。
この連載では、UNIX系OSをコマンドライン上から効率よく利用するために知っておくべきテクニックや、便利な小技を紹介していきます。
今回は第0回ということで、今後便利なUNIXコマンドを紹介するにあたり、知っておきたい「シェルの制御構造」や「正規表現」などを、例を交えながら簡単に復習していきます。
スポンサードリンク
なるべくUNIX系OS一般に当てはまるよう解説するので、特に記述がない場合は、Mac OS X、Red Hat Linux上でも動作します。
シェルに関しては、bash(Bourne-Again Shell 2および3)を想定しています。
コマンドが左から右に順に実行され、前のコマンドの出力(標準出力)が次のコマンドに入力(標準入力)されます。
コマンド1 | コマンド2 | ...
コマンドが左から右に順に実行されます。「;」(セミコロン)で区切ることで、1行に複数のコマンドを書くことができます。
コマンド1 ; コマンド2 ; ...
次のように、コマンドを「&&」で区切って書くと、前のコマンドが成功した場合(終了コードが0の場合)のみ、後ろのコマンドが実行されます。
コマンド1 && コマンド2
コマンド1が正常終了以外(終了コードが0以外)だったら、コマンド2を実行されます。
コマンド1 || コマンド2
次の例では、一般的なコンパイル手順である「./configure」「make」「make install」を順番に実行します。途中でエラーが発生した場合、それ以降のコマンドは実行されません。このため不要なコンパイル時間の短縮になります。
$ ./configure && make && make install
「$()」で囲まれた部分は、そのコマンドの実行結果に置き換えられます。例えば、現在の日時のファイルを新規作成するには、touchコマンドを使用して次のように記述できます。
$ touch $(date +%F) $ ls 2008-09-05
dateコマンドは、現在の日時を表示します。また「+%F」オプションを付けることで、「yyyy-mm-dd」形式の日時を表示しています。
※注1 コマンド置換は「`...`」(バッククォート)を使って、次のようにも記述できます。 $ touch `date +%F` ただし、ネストしやすい・読みやすいという理由から、bashでは「$()」を使うことが多いようです。
「,」カンマで区切った複数の文字列を「{}」ブレース(中括弧)で囲む事で、囲んだ文字列を展開することができます。
複数のディレクトリを一度に新規作成するには、mkdirコマンドを使用して次のように記述できます。
$ mkdir {dir1,dir2,dir3} $ ls dir1/ dir2/ dir3/
また、次のようにディレクトリ名に対して、部分的に中括弧を利用することもできます。
$ mkdir dir{1,2,3} $ lsdir1/ dir2/ dir3/
このように、異なる文字列を複数個指定する場合に役立ちます。
変数から値を取り出すときに使われるのが変数展開です。「$変数名」または「${ 変数名 }」のように使います。
代表的な4種類を紹介します。
変数展開 | 説明 |
---|---|
${パラメータ#パターン} | パラメータ前方から、パターンに最短一致した部分を除く |
${パラメータ##パターン} | パラメータ前方から、パターンに最長一致した部分を除く |
${パラメータ%パターン} | パラメータ後方から、パターンに最短一致した部分を除く |
${パラメータ%%パターン} | パラメータ後方から、パターンに最長一致した部分を除く |
次のように、拡張子の除去やファイル名の取得などで、よく使われます。
$ mypath=/home/hoge/foo.txt $ echo ${mypath%.*} → 拡張子を除く /home/hoge/foo $ echo ${mypath##/*/} → ファイル名のみを得るfoo.txt
bashは単純なコマンドだけではなく制御文も受けつけます。制御構造を使うと定型的な処理を行うのが楽になります。
シェルで繰り返しを行うにはfor文、while文、until文などがあります。ここでは、コマンドライン上でよく利用されるfor文、while文を紹介します。
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
※注2 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に続けて条件を記述します。処理内容は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という変数に取り込みます。
※注3 古いバージョンのfindコマンドを利用している方は、 「-print」オプションを追加しないと正しく動作しません。
シェルの条件分岐にはcase文と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/\.[^.]*$//'test1test2test3test4
「sed 's/\.[^.]*$//'」とすれば最後のピリオド以降行末までが削除されます。また、入力がピリオドを含まない(拡張子を持たない)場合は、何も行いません。このコマンドであれば、拡張子が不定の場合でも使用可能です。
今回は、覚えておくべきシェルの制御構造、正規表現に関して簡単に説明しました。
次回からは、これらのコマンドや制御構造を組み合わせて、業務で楽できるようなテクニックをテーマ別に紹介します。
スポンサードリンク