シェル芸勉強会入門編をやってみたいと思う今日この頃だが、どんな感じにすればいいのか?なかなか難しいなあと思っているところである。
シェル芸の発想はやはりストリーム的な処理だ。データを変数に持たせて処理するというより、データをパイプでストリーム処理というのが本質ではないだろうか?
ということで、題材として考えたのが眠れない方へ羊が100匹まで数えるシェル芸。下記のような出力を、1行毎に1秒間をおいて出力することを考える。
羊が1匹 羊が2匹 羊が3匹 .,.. 羊が98匹 羊が99匹 羊が100匹
変数とループ構造を使っての手続き型な書き方から、ストリーム的な書き方へ。下記のような流れで考えてみよう。ちなみにbashを使ったワンライナーである。csh系のシェルを使っていらっしゃる方はbashを起動。
カウントアップで100まで
手続き型で素直に書くとしたら下記の通りだろうか?
$ n=1;while [ $n -le 100 ]; do echo '羊が'$n'匹'; sleep 1; n=`expr $n + 1`; done
ワンライナーで見づらいかもしれないので、シェルスクリプトにしたのがこちら。bash独自の機能は使わず、Bourne Shellの機能のみを利用。シェルの文法やexprコマンドについては割愛する。
#!/bin/sh n=1 while [ $n -le 100 ] do echo '羊が'$n'匹' sleep 1 n=`expr $n + 1` done
bashの数値計算機能を使ったのがこちら。exprコマンドが無くなったこと以外は変わらず。
$ n=1;while [ $n -le 100 ]; do echo '羊が'$n'匹'; sleep 1; n=$((n+1)); done
1から100までの数列を使う
forループに1から100までの数列を与えて処理する。seqコマンドで数列を作成。
$ for n in `seq 100`; do echo '羊が'$n'匹'; sleep 1; done
bashのブレース展開で数列を作成してもいいだろう。
$ for n in {1..100}; do echo '羊が'$n'匹'; sleep 1; done
seqコマンドで数列を作成して、パイプでwhileループへ渡すパターン。(これは罠が有るので使う際は要注意)
$ seq 100 | while read n; do echo '羊が'$n'匹'; sleep 1; done
xargsで繰り返し処理
xargsを使うと制御構文を使うことなく繰り返し処理が出来る。ストリーム的な処理として記述しやすくなる。今回は1行ずつ処理するため、-I
オプションを使うのがポイントだ。
$ seq 100 | xargs -I@ sh -c 'echo 羊が@匹; sleep 1'
seqコマンドは-f
オプションでフォーマット指定が出来る。
$ seq -f '羊が%g匹' 100 | xargs -I@ sh -c 'echo @; sleep 1;'
シェルもコマンドの一つ
UNIXのシステムから見ると、シェルも一つのコマンドに過ぎないのだ。よって100までの数列から実行するコマンド列を作成してやって、パイプでシェルに渡してしまえばよい。
$ seq -f 'echo 羊が%g匹; sleep 1' 100 | sh $ seq 100 | sed 's/^/echo 羊が/' | sed 's/$/匹; sleep 1/' | sh
こんな感じで、手続き型的な発想からストリーム処理的な発想に慣れていけると、シェル芸人へのステップアップが出来るのではないかと思う。