ユニバーサル・シェル・プログラミング研究所では、シェルスクリプトに制御構文が増えることを極力避けるようにさまざまな々な工夫をしている。制御構文を避ける理由はコードが読みにくくなるためだ。ユニバーサル・シェル・プログラミング研究所との業務ではじめてシェルやシェルスクリプトを始めたというケースでは、最初に経験するプログラミングがユニケージ開発手法になるという方が少なくないが、やはり制御構文が最初の壁になることが多い。
制御構文はシェルスクリプトが便利だと感じるようになってから使いはじめるくらいでも良い。もしかすると、便利だと思ってからの方がすんなり頭に入るかもしれない。以降では、どのように制御構文の使用を避けるのかを紹介する。
次のソースコードを見てみよう。ユニケージ開発手法においてはまず目にすることのないソースコードだ。
$ cat countup.sh #!/bin/sh n=1 while [ $n -le 10 ] ; do echo $n n=$((n+1)) done $
このソースコードを実行すると次のような結果が得られる。
$ ./countup.sh 1 2 3 4 5 6 7 8 9 10 $
この処理はコマンドseq(1)を使えば次のように処理することができる。
$ seq 1 10 1 2 3 4 5 6 7 8 9 10 $
利用するシェルをbashに限定するなら、次のような記述方法もある。
$ echo {1..10} | tr ' ' 'n' 1 2 3 4 5 6 7 8 9 10 $
シェルの機能を使ってcat(1)のようなコマンドを実装すると次のようになる。ここでは行番号を出力するようにしており、cat(1)に-nオプションを指定した場合のような振る舞いになる。
$ cat file 山田 上田 田中 $ cat cat.sh #!/bin/sh n=1 while read line ; do echo $n $line n=$((n+1)) done $ cat file | ./cat.sh 1 山田 2 上田 3 田中 $
ファイルから1行読み込んでシェル変数に入れるという処理はかなり遅い作業である。ここは次のように書いた方が高速になる。
$ cat cat2.sh #!/bin/sh awk '{print NR,$0}' $
ターミナルで直接実行するなら、次のように記述できる。
$ cat file | awk '{print NR,$0}' 1 山田 2 上田 3 田中 $
TukubaiまたはOpen usp Tukubaiに用意されているコマンドjuni(1)を使うともっと簡単に実現できる。
$ juni file 1 山田 2 上田 3 田中 $
削除候補となるファイルのリストを作ってから、逐一削除するような作業があったとする。たとえば次のようなソースコードになる。
$ cat rmall.sh #!/bin/sh cat filelist | while read file ; do rm "${file}" done $
これはxargsを知っていれば次のように簡単に済む。
$ cat rmall2.sh #!/bin/sh cat filelist | xargs rm $
xargs(1)は入力1つ1つに対して処理を実施することもできる。たとえばバックアップを取るといった次のソースコードがあったとする。
$ cat backupall.sh #!/bin/sh cat filelist | while read file ; do cp $file $file.old done $
xargs(1)の-Iオプションを使うと次のように記述できる。
$ cat backupall2.sh #!/bin/sh cat filelist | xargs -I % cp % %.old $
次のようにdate(1)を使うと日付のリストを作ることができる。これはFreeBSDやMac OS XなどのBSD date(1)コマンドを使った場合の記述となる。
$ cat datelist.sh #!/bin/sh d=20100101 while [ $d -lt 20100401 ] ; do echo $d d=$(date -v+1d -j -f "%Y%m%d" "$d" "+%Y%m%d") done $
LinuxなどGNU Core Utilitiesのdate(1)コマンドを使っている場合には次のようになる。
$ cat datelist.sh #!/bin/sh d=20100101 while [ $d -lt 20100401 ] ; do echo $d d=$(date -d "$d 1 day" +%Y%m%d) done $
date(1)コマンドは-uオプションと+フォーマット指定以外はPOSIX.1に規定されていないため、ツールごとにオプションが異なっている。BSD date(1)のオプションについてはdateコマンドの便利な使い方 : BSD dateを、GNU date(1)のオプションに関してはdateコマンドの便利な使い方 : GNU Core Utilities dateを参照のこと。
date(1)コマンドにおけるフォーマット指定に関してはdateコマンドのフォーマット一覧 : IEEE Std 1003.1 "POSIX.1"に説明がまとまっている。
TukubaiまたはOpen usp Tukubaiに用意されているmdate(1)コマンドを使うともっと簡単に済ませることができる。
$ mdate -e 20100101 20100401 | tr ' ' 'n' | head 20100101 20100102 20100103 20100104 20100105 20100106 20100107 20100108 20100109 20100110 $
tr(1)のこうした使い方をする場合、ユニケージ開発手法ではtarr(1)を使って次のように記述する。
$ mdate -e 20100101 20100401 | tarr | head 20100101 20100102 20100103 20100104 20100105 20100106 20100107 20100108 20100109 20100110 $
通常のプログラミングではよく使われる多重ループだが、シェルスクリプトではできるだけ避けたい。例えば次のような2重ループの処理があったとする。
$ cat loop.sh #!/bin/sh for a in A B C ; do for n in 1 2 3 ; do echo $a $n done done $ ./loop.sh A 1 A 2 A 3 B 1 B 2 B 3 C 1 C 2 C 3 $
Open usp Tukubaiにはloopx(1)というコマンドがあり、こうしたリスト作成の代替処理として利用できる。
$ head a n ==> a <== A B C ==> n <== 1 2 3 $ cat loop2.sh #!/bin/sh loopx ./a ./n | while read a n ;do echo $a $n done $ ./loop2.sh A 1 A 2 A 3 B 1 B 2 B 3 C 1 C 2 C 3 $
これまで制御構文whileやforのはずし方について書いてきた。処理速度を高速化するためにwhileやforをコマンドに置き換えるというのは、それなりの効果が期待できる方法だ。シェルスクリプトに制御構文であるforやwhileが出てきたら、何か避ける方法がないか探してみるとより高速なスクリプトが書けるようになるかもしれない。
※ ユニケージはユニバーサル・シェル・プログラミング研究所の登録商標。
※ usp Tukubaiはユニバーサル・シェル・プログラミング研究所の登録商標。
※ 2012年7月から始まる教育講座に合わせて、従来の「ユニケージ」または「ユニケージ方法論」は「ユニケージ開発手法」へ名称を統一。