シェルスクリプトによるデータ処理
(一部工事中、順次拡張予定)
シェルスクリプトの基本
シェルスクリプトは、言語というよりはコマンド群のようなもので、 CやFortranと異なりコンパイルせずに使うことができる。 ここでは、大概のUNIXシステムで使うことのできるBシェルを扱う。シェルスクリプトの一番簡単な使い方は、ただコマンドを羅列することである。
#!/bin/sh echo "This is B-shscript." ls cd ..上記のファイルを例えばtest.shとする。実行するには、ファイルに実行許可を与えてからファイル名を打つ。
% chmod +x test.sh % test.sh (環境によっては、% ./test.sh)echo文が実行され、"This is B-shscript."という出力がなされ、ディレクトリ内のファイル一覧が出され、最後にディレクトリが一つ上がったはずである。 一行目がシェルスクリプトを示す宣言となるので、必ず入れよう。その宣言文の後に、自分の好きなコマンドを書けばよい。例えば、夜自宅に帰る前に、CALC1・CALC2というプログラムを走らせておきたい場合は、
#!/bin/sh CALC1 CALC2として、% test.sh&としておけば、次の朝に(計算が終わっていれば)結果が出ていることになる。なお、#を付けると、それより後ろは改行までコメント文として認識される。
シェルスクリプトの書き方
もう少し複雑なことを実行させてみよう。なお、今後例として示すファイルは、aionまたはshannonの~nogu/Lecture/shscriptに置いてあるので、適宜自分のディレクトリにコピーして使ってよい(以下同様)。最初の例は、引数をチェックして取り込む基本的なシェルスクリプトである。
ファイル名:basic.sh
#!/bin/sh # #--- 引数のチェック ### $#はコマンドラインに続く文字列の数。 ### もし引数の個数が一つでなければ、usageを標準エラー出力に出して終了 if [ $# != 1 ]; then echo "usage: $0 strings" 1>&2 exit 0 fi #--- 変数ARGV1に第一引数を代入 ARGV1=$1 #--- ARGV1を画面に(標準出力に)出す echo $ARGV1入力した文字列をそのまま返している。 if文内では引数の解析を行い、引数の個数が一つであるとき以外は使用法を表示して終了する。 次に、変数ARGV1に引数を代入し、echo文でARGV1を出力している。以下が実行例である。% basic.sh aaa aaaここで、シェルスクリプトにおける変数の約束ごとを挙げる。のとき、${AAA}${i}と書くと、data001となる。
- 変数は、宣言せずにいきなり勝手に使える。アルファベットと数字の組合せ。ただし空白はダメ。
例: AAA、AAA_2、i_j_5
- 変数は、文字列として扱われる。数字もアスキー文字となる。C言語のChar型変数だと思えばよい。
- 変数の代入は、=を使う。=の前後に空白を入れてはいけない。=と変数はくっつけること。
例: AAA=test ←変数AAAに文字列testを代入
- 変数の呼出時は、変数の頭に$を付ける。
例: BBB=$AAA ←変数BBBに変数AAAの中身を代入
もし、「BBB=AAA」とすると、変数BBBにはAAAという文字列そのものが代入される。
- 変数は{}で括ると、変数名の範囲を指定できる。
例: 変数AAAと変数iをくっつけたい時、AAAiと書くと、新たな変数として認識されてしまう。
くっつけたい場合は、${AAA}${i}と書く。例えば、AAA=data i=001
for文
次の例は、for文の例である。 ファイル名:for.sh#!/bin/sh #--- 引数チェック、格納。使用方法表示。 if [ $# != 0 ]; then echo "usage: $0" 1>&2 exit 0 fi #--- for文による繰返し #### 値の代入 for NUM in 0 1 2 3 ; do echo "$NUM" done実行結果は% for.sh 0 1 2 3この例文では、for文で変数NUMに0から3までの文字を順に代入してその文字を出力している。 inの後に続くのは、例えばfor AAA in 1 3 5 7 9 ; do for AAA in a b c d e ; do for AAA in cat dog pig ; doのように、文字でさえあれば何でも良い。これらの例では、全てAAA変数にその後に羅列してある文字が代入されながらループ文が回る。
while文
for文では、変化させる文字を全て書かないとならないので、単に変数を1ずつ増やしていきたいときには面倒である。次の文は、変数iを1ずつふやしていくものである。ファイル名:while.sh
#!/bin/sh # #0からMAXまでの値を順に表示する # #--- 引数チェック、格納。使用方法表示。 ### $#はコマンドラインに続く文字列の数。 if [ $# != 1 ]; then echo "usage: $0 MAX" 1>&2 exit 0 fi MAX=$1 #--- while文による繰返し ### インクリメント(変数を1ずつ増やしていく) ### whileの次の[...]は条件文と呼ばれ、この値がゼロ(真)の時に命令を実行する ### 以下の例文は、$iが$MAX以下である時に真となり、do以下の文が実行される i=0 while [ $i -le $MAX ]; do echo "$i" 1>&2 i=`expr $i + 1` #expr文、整数の四則演算ができる done出力結果は% while.sh 5 0 1 2 3 4 5ここで、exprコマンドは、簡単な整数の四則演算をさせるコマンドである。 また、while文内の[]は条件部で、この値がゼロ(真)の時にdo以下の命令が実行される。 整数を判断する条件部には、例えば次のようなものがある(右のコメントの条件が満たされた場合に、真となる。)while [ $i -eq $NUM ] ←整数iとNUMは等しいとき while [ $i -ge $NUM ] ←整数iはNUM以上であるとき while [ $i -gt $NUM ] ←整数iはNUMよりも大きいとき while [ $i -le $NUM ] ←整数iはNUM以下であるとき while [ $i -lt $NUM ] ←整数iはNUMよりも小さいとき while [ $i -ne $NUM ] ←整数iとNUMは等しくないときなお、文字列が等しいかを判断するのは、-eqではなくて=なので注意すること。 while文では、次のような処理も可能である。入力ファイルには、3列のデータを使う(test.datとして用意してある)。ファイル名:while_read.sh
#!/bin/sh #--- 引数チェック、格納。使用方法表示。 ### $#はコマンドラインに続く文字列の数。 ### FILENAMEは、入力したいファイル名 if [ $# != 1 ]; then echo "usage: $0 FNAME" 1>&2 exit 0 fi #--- exec文。 #### 引数$1のファイルを標準入力に変更。read文での読み込みを可能にする exec 3<&0 <$1 #--- while文による繰返し #### read文で標準入力からデータを一行ずつ読み込む #### echo文で1列目と3列目のみを標準出力に出力 while read COLUMN1 COLUMN2 COLUMN3 ; do echo "$COLUMN1 $COLUMN3" done
if文
すでにif文は出てきているが、機能の例を挙げて詳しく解説しておく。 条件の前に!をつけると、直後の条件を否定する。
- ファイルの存在チェック
if [ -f $FNAME ]; then echo "$FNAME はある" 1>&2 fi if [ ! -f $FNAME ]; then echo "$FNAME はない" 1>&2 fiディレクトリの存在チェック if [ -d $DIRNAME ]; then echo "$DIRNAME はある" 1>&2 fi if [ ! -d $DIRNAME ]; then echo "$DIRNAME はない" 1>&2 fiAND条件文、else文 if [ -f $FNAME1 -a -f $FNAME2 ]; then echo "$FNAME1 と $FNAME2 がある" 1>&2 else echo "$FNAME1 か $FNAME2 がない" fiディレクトリがない場合、親ディレクトリも含めて一気に生成
(mkdir -pオプション)if [ ! -d $DIRNAME1 -o ! -d $DIRNAME2 ]; then echo "$DIRNAME1 か $DIRNAME2 がないので、生成" 1>&2 mkdir -p $DIRNAME1 $DIRNAME2 fi文字列判断
-eqではなく、=を使う(=は一つしか使わないことに注意!)if [ $ARG = "GOME" ]; then echo "ARG is GOME" 1>&2 fiなお、while文で出てきた[]の条件部は、if文にもそのまま使える。
コマンドの実行結果の変数への代入
変数に何かのコマンドの実行結果を代入することができる。#!/bin/sh AAA=`ls` echo "$AAA"``は、シングルコーテーション('')ではないことに注意。``の中には、普通にUNIXの端末で打つコマンドを何でも入れることができる。
応用例 シェルからgnuplotを走らせる
次の例文は、gnuplotを用いてsin関数をプロットさせるスクリプトだが、 gnuplot内で使う変数、つまり図のタイトルや描画するxの範囲をUNIXコマンドの引数から与えられるところがミソである。ファイル名:gnuplot.sh
#!/bin/sh # #title, 横軸の最小値、最大値を引数に入れると、 #gnuplotによるsin(x)のプロットを行ない、ポストスクリプトで保存する #--- 引数チェック、格納。使用方法表示。 ### $#はコマンドラインに続く文字列の数。 if [ $# != 3 ]; then echo "usage: $0 TITLE XMIN XMAX" 1>&2 exit 0 fi ### 引数は、図のタイトル、xの最小値、xの最大値の3つ。 TITLE=$1 XMIN=$2 XMAX=$3 #---シェル内でのgnuplot起動 #### EOF行までの間に空行を挟んではいけない。 gnuplot <<EOF set out "sin.ps" set term post land set xr [$XMIN:$XMAX] set title "$TITLE" plot sin(x) EOFうまくできたかは、gvで確認する。%gv sin.ps「gnuplot <<EOF」行から最後の「EOF」行までの間にgnuplotのコマンドを書いておく。 空行をあけることはできないので注意する。 どうしてもあけたい場合は、下のように#ではじまるコメント行を入れると良い。gnuplot <<EOF #--------- # Filename #--------- # set out "sin.ps" # # #--------- # Terminal #--------- # set term post land set xr [$XMIN:$XMAX] set title "$TITLE" plot sin(x) EOF