シェルスクリプトは、シェルコマンドをファイルに羅列して、一括に実行するものである。
本講義では、bash を元に説明を行う。
以下に、.cc ファイルのそれぞれ、行数と、総行数を表示するシェルスクリプトの例を示す。
#!/usr/bin/env bash
sum=0
for i in *.cc
do
lines=`wc -l $i | awk '{print $1}'`
echo $i: $lines
sum=`expr $sum + $lines`
done
echo sum: $sum
そのシェルの中でだけ利用される変数
上記の例におけるsumや、linesはシェル変数である。
シェル変数は、変数名=値 の形式で記述する。
値を取得する場合には、$変数名 で指定する。
呼び出された子プロセスにも引き渡される変数
コマンド実行パスを示すPATHなどが例になる。
環境変数は、 export 変数名=値 の形式で記述する。
また、 変数名=値 コマンド と記載することで、指定したコマンドに対して
環境変数を渡すことが出きる。
値を取得する場合には、$変数名 で指定する。
シェルには、いくつかの組み込み変数が存在する。 ここでは、シェルスクリプトで良く使われる組み込み変数を示す。
| 変数名 | 用途 |
|---|---|
| ${数値} | シェルスクリプトにて指定された引数を示す。 $1 には、1番目の引数が格納されている。 |
| $# | シェルスクリプトで指定された引数の数を示す。 |
| $@ | シェルスクリプトで指定された引数の一覧をスペース区切りで示す。 |
| $0 | 実行しているシェルスクリプト名 |
| $? | 直前に実行したコマンドの終了ステータス |
| $$ | 実行してるシェルスクリプト自身のプロセスID |
シェルスクリプトでは、処理の流れを指定する制御文が用意されている。
シェルスクリプトでは以下の構文によって処理の分岐を実行できる。
if {コマンド}
then
{真判定での実施コマンド}
else
{偽判定での実施コマンド}
fi
ifで指定したコマンドの終了ステータスが0の場合に、真判定での実施コマンドが実行され 終了ステータスが0以外の場合に、偽判定の実施コマンドが実行される。
以下の記載で多重分岐の記述も可能
if [ 条件1 ]
then
処理1
elif [ 条件2 ]
then
処理2
else
処理3
fi
上記は、もし「条件1」に当てはまるなら、「処理1」を実行する。 それ以外で「条件2」に当てはまるなら「処理2」を実行する。 それ以外は「処理3」を実行するという意味である。
なお、 if文の横に指定してる [ ] は test コマンドと呼ばれるもので、
シェルスクリプト内での論理条件を記述するために用意されているコマンドである。
以下に良く使われる。 test コマンドの記述をしめす。
| 記述方式 | 判定内容 |
|---|---|
| [ 値1 = 値2 ] | 値1 と 値2 が同じ文字列の場合に真 |
| [ 値1 != 値2 ] | 値1 と 値2 が異なる文字列の場合に真 |
| [ -n 値 ] | 値が1文字以上の文字列 |
| [ -z 値 ] | 値が空文字列 |
| [ 値1 -eq 値2 ] | 値1 と 値2 が同じ整数値の場合に真 |
| [ 値1 -ne 値2 ] | 値1 と 値2 が異なる整数値の場合に真 |
| [ 値1 -lt 値2 ] | 値1 が 値2 より小さい場合に真 |
| [ 値1 -le 値2 ] | 値1 が 値2 以下の場合に真 |
| [ 値1 -gt 値2 ] | 値1 が 値2 より大きい場合に真 |
| [ 値1 -ge 値2 ] | 値1 が 値2 以上の場合に真 |
| [ ! 条件式 ] | 条件式の結果が 偽 の場合に真 |
| [ 条件式1 -a 条件式2 ] | 条件式1、 条件式2 のいずれも真の場合に真 |
| [ 条件式1 -o 条件式2 ] | 条件式1、 条件式2 のいずれかが真の場合に真 |
| [ -e パス ] | パスのエントリが存在すれば真 |
| [ -f パス ] | パスのエントリが存在してレギュラーファイルであれば真 |
| [ -d パス ] | パスのエントリが存在してディレクトリであれば真 |
| [ -L パス ] | パスのエントリが存在してシンボリックリンクであれば真 |
| [ パス1 -nt パス2 ] | パス1の更新日時がパス2よりも新しければ真 |
| [ パス1 -ot パス2 ] | パス1の更新日時がパス2よりも古ければ真 |
シェルスクリプトでは文字列のパターンマッチによる処理分岐を以下の構文によって行う。
case {文字列} in
{パターン1})
{処理1}
;;
{パターン2})
{処理2}
;;
{パターン3})
{処理3}
;;
esac
case文では、指定された文字列がパターンに合う場合に、対象の処理が実行されます。
パターンマッチングは上から順に行なわれ、シェルのワイルドカードも指定することが可能である。
このため、 どのパターンにも正合しない場合の処理のパターンは *) を指定することで行う。
また、パターンを | で繋ぐことで、複数のパターンでのマッチングを記述することが可能である。
たとえば、 aaa|bbb) と指定することで 文字列が aaa もしくは bbb とマッチした場合の
処理分岐が行える。
シェルスクリプトではリストを繰り返し実行するために以下の構文をつかう
for {シェル変数} in {リスト}
do
{処理}
done
do〜doneのなかでは、for で指定したシェル変数が、リスト順に変更されなが、リスト件数分実行されます。
リストの区切り文字は、シェル変数 IFS に指定されている文字が使用されます。
また、bash では、 {開始..終了} で連番のリストを生成できます。
for i in {1..10}
do
echo $i
done
上記の処理で 1 〜 10 が順に表示される
コマンド結果が真の間繰り返しを行う構文は以下となる。
while {コマンド}
do
{処理}
done
コマンドの結果が真の場合に処理が実行される。
コマンドには、test コマンドが一般的に使われる。 他にかなず真を返す true や、
偽を返す false がある。
コマンド結果が偽の間繰り返しを行う構文は以下となる。
until {コマンド}
do
{処理}
done
コマンドの結果が偽の場合に処理が実行される。
シェルスクリプトには、別環境を疑似的に実現するため、サブシェルといった機能がある。
tar cvf - * | (cd ../new; tar xvf -)
上記のスクリプトでは、 括弧の中でディレクトリ移動をしているが括弧の中実行後に 元の環境に戻るので、全体として記述が楽になります。
なおサブシェルにはシェル変数などの値は引き継がれます。
シェルスクリプトでは、引用符によって文字列の展開内容が変化する。
以下に、各引用符による展開内容の違いを示す。
以下に変数展開についての一覧を示す。
| 展開記述 | 概要 |
|---|---|
| $val | valという変数の展開を行う |
| ${val} | valという変数の展開を行う |
| ${val:-default} | valという変数の展開を行う valに値が無ければ default を展開する |
| ${val:=default} | valという変数の展開を行う valに値が無ければ default を展開し、かつ、変数へ代入する |
| {val-default} | valという変数の展開を行う valが未定義であれば default を展開する(定義があって値がないばあいは空を展開する。) |
| ${val=default} | valという変数の展開を行う, valが未定義であれば default を展開し、かつ、変数へ代入する(定義があって値がないばあいは空を展開する。) |
| ${val:?} | valという変数の展開を行う valに値が無ければエラーとなる |
| ${val?} | valという変数の展開を行う valが未定義であればエラーとなる |
| ${val:?error} | valという変数の展開を行う valに値が無ければエラーとなり、error がメッセージとして表示される |
| ${val?error} | valという変数の展開を行う valが未定義であればエラーとなり、error がメッセージとして表示される |
| ${val:offset:length} | valという変数のoffsetからlength文字を展開するlengthの指定がない場合には末尾までが対象となる |
シェルスクリプトでは、複数行のドキュメントを表現するのに ヒアドキュメントという記述方法を利用する。
ヒアドキュメントの記述方法を以下にしめす。
cat >>EOS
hoge
fuge
piyo
EOS
コマンドの記述ないに >>{ラベル} と指定することで、次行から{ラベル}の手前までを
ドキュメントとして扱われます。
変数展開やエスケープシーケンスもそのまま利用できます。
findコマンドは、指定したディレクトリ配下から条件に合致するファイルを検索して、
指定した処理を実行します。
■ コマンド概要
find [-H | -L | -P] path ... [expression]
path で指定したディレクトリ配下を再帰的に検索して、expression 指定した処置をおこなう。
■ オプション
-H
pathに指定されているエントリが、シンボリックリンクだった場合に、対象のリンク先を参照する。
-L
再帰検索によってシンボリックリンクを見つけた際に対象のリンク先を参照する。
-P
シンボリックリンクのリンク先参照をおこなわない。 (デフォルト)
■ expression
以下によく使われる expression を示す。
探索条件
| expression | 用途 |
|---|---|
-name pattern |
エントリ名がpatternのエントリを検索する。 patternにはシェルのワイルドカードも指定することが可能である。 |
-type type |
typeで指定したタイプのエントリを検索する。 typeに指定されるのは以下の通り d ディレクトリ f 通常のファイル l シンボリックリンク s ソケット p 名前付きパイプ (FIFO) |
-regex pattern |
エントリ名が正規表現のpatternにマッチするエントリを検索する。 |
-atime n |
エントリの参照日時が n 日前のエントリを検索する。 (起点は前日になる) |
-ctime n |
エントリの変更日時が n 日前のエントリを検索する。 (起点は前日になる) |
-mtime n |
エントリの更新日時が n 日前のエントリを検索する。 (起点は前日になる) |
-user uname |
所有者が uname なエントリを検索する。 |
-group gname |
所有グループが gname なエントリを検索する。 |
-readable |
読み取り権を持つエントリを検索する。 |
-writable |
書き込み権を持つエントリを検索する。 |
-executable |
実行権を持つエントリを検索する。 |
論理式
| expression | 用途 |
|---|---|
| ! expr | expr の条件が偽となるエントリを検索する。 |
| expr1 -a expr2 | expr1, expr2 いずれの条件にも当て嵌まるエントリを検索する。 |
| expr1 -o expr2 | expr1, expr2 いずれかの条件にも当て嵌まるエントリを検索する。 |
アクション
| expression | 用途 |
|---|---|
-print |
条件にマッチしたエントリを改行で区切って表示する。 |
-print0 |
条件にマッチしたエントリをヌル文字で区切って表示する。 (xargs -0 に対応) |
-exec command ; |
検索されたファイルに対して ファイル毎に command を実行する。 {} の位置に対象のファイルのパスが展開される。 |
-exec command {} + |
検索されたファイルに対して、 command を実行する。 コマンドは一度の実行として扱われ、{} に検索されたすべてのファイルが展開される。 |
■ 使用例
./src 配下のソースファイルとヘッダファイルの更新日時を今の時間に変更する例を示す。
find -L ./src \( -name "*.[ch]" -o -name "*.[ch]pp" \) -print -exec touch \{\} \;
awkコマンドは、標準入力や、ファイル内の文字列を1行ずつ、指定されたプログラムにて処理をおこなう。
シェルスクリプトでは、区切り文字で分割された各フィールドに対する処理を行うツールとして利用される。
■ コマンド概要
awk [ -F fs ] [ -v var=value ] [ 'prog' | -f progfile ] [ file ... ]
標準入力 もしくは、file で指定したテキストファイルに対して prog で指定した処理を行う。
■ オプション
-F fs
フィールドを分割するセパレータ文字を指定する。fs に指定された正規表現にマッチする文字列をセパレータと判断する。
-v var=value
prog で記述された処理で利用する変数 var の初期値として value を定義する。
-f progfile
適用する処理を記述したファイルを porgfile として指定する。 -f オプションを指定した場合は、 prog による直接の処理指定は行えない。
■ 処理記述
awk に指定する処理は、以下のフォーマットで記述を; もしくは、 改行 で区切って指定する。
pattern { action }
pattern には、処理対象とする行を特定するためのパターンを指定する。
action には、pattern でマッチした行に対して実行する処理を awk programing で記述する。
pattern が未指定の場合には、全ての行に対して action を実行する。
awk では、対象行の各フィールドをフィールド変数で参照することが出来る。たとえば $0は
行全体を差し、 $1, $2 はそれぞれ第1フィールド、第2フィールドの値を返す。
例えば、
awk -F "," '$2 < $3 {print $0}' < input.txt
と指定すると第2カラム目が、第3カラム目より小さい行を表示することになる。
pattern, action の記述方法については、awk のmanデータを参照のこと
以下にシェルスクリプトで使う pattern 記述と action で使用するコマンド について示す。
■ pattern 指定について
| pattern | 概要 |
|---|---|
| /regix/ | regix に指定された正規表現とマッチする行を対象とする。 |
| /start/,/end/ | start とマッチする行から、 end にマッチする行までを対象とする。 |
| 比較式 | 比較式に指定された条件式にマッチする行を対象とする。 比較式には length のようなコマンドの指定も可。 |
| BEGIN | 1行目の実行前に指定のアクションを実行する。 |
| END | 最終行実行後に指定のアクションを実行する。 |
■ よく使うコマンド
action では、変数や四則演算などが利用することができる。 また、以下のようなコマンドを利用することもできる。
| コマンド名 | 用途 |
|---|---|
| print string | string に指定した内容を標準出力に出力する。 |
| length(string) | string に指定した内容の文字数を取得する。 |
| tolower(string) | string に指定した文字列を小文字変換する。 |
| toupper(string) | string に指定した文字列を大文字変換する。 |
sedは、標準入力やファイル内の文字列を1行ずつに対して、編集を行うことが出来る。
編集結果は標準出力に出力される。
■ コマンド概要
sed [-Ean] command [file ...]
sed [-Ean] [-e command] [-f command_file] [-i extension] [file ...]
標準入力 もしくは、file で指定したテキストファイルに対して command, command_file で指定した編集を行う。
■ オプション
-E
正規表現を、Basic Regular expression (BRE) ではなく、 拡張 (モダン) 正規表現として解釈する。
-e command
編集コマンド command をコマンドリストに追加する。
-f command_file
ファイル command_file に記述されたコマンドをコマンドリストに追加する。 編集コマンドは 1 行ごとに記述する。
-i extension
編集結果を入力ファイルに適用する。 この際に、オリジナルファイルに.extension を追加したバックアップファイルが作成される。 extension 指定がない場合には、バックアップは行なわれない。
■ コマンド記述
sedで指定するコマンドは [位置指定]編集コマンド のフォーマットで行う。
1,3s/abc/efg/g
上記の例では、 1,3 が位置指定で、s/abc/efg/g が編集コマンドになる。(1〜3行目の "abc" を "efg" に置換する)
位置指定には、カンマ区切りでの行範囲指定のほかに、//で囲んだ正規表現指定が可能。
以下に良く使う編集コマンドを示す。
| 編集コマンド | 用途 |
|---|---|
| s/regexp/replace/flag | regexp の正規表現にマッチする文字列を replace に置換する。 flags には、以下のものを0個以上指定できる。 N パターンスペースで N 回目にマッチした regexp のみを置換します。 g 先頭だけではなく、重なりあわない全てのマッチした内容を replace で置換する。 p 置換が行われたら、パターンスペースの内容を標準出力に書き出す。もし、置換後の内容が置換前のものと同一でも置換が行われたとみなす。 |
| d | 指定した行の削除を行う。 |
| c text | 指定した行をtextに書き換える |
expr はコマンドラインを算術式と判断して、その計算結果を標準出力に返す。
bashやzsh だと $((演算式)) で記述できるので、利用するシェルが明確であるのであれば、そちらを使うのもあり。
例:(シェル変数 i のカウントアップ)
i=`expr $i + 1`
もしくは
i=$((i+1))
seq は連続する数値列を標準出力に返す。
seq のコマンドパラメ=タは以下のとおり
seq [start [incr]] last
start には、開始値、incr には、 増分値 last には終了値を指定する。
start, incr の規定値は 1 である。
たとえば、
seq 2 2 12
だと、以下の出力になる。
2
4
6
8
10
12
シェルスクリプトでは以下のように for文 で使われることが多い
for i in `seq 1 4`
do
echo $i
done
bash, zshなどでは、 {1..4} で同様の記述が可能です。