双六工場日誌

平凡な日常を淡々と綴ります。

シェルスクリプトの中で1行ずつ変数を分割する際には、cutとかawkとか余計なプロセスを起動せずsetを使って分割した方が効率的

シェルスクリプトの中で、スペース区切りもしくはタブ区切りのレコードを扱うことがよくあると思います。

たとえば、前回のエントリ「AWS CLIとjqを使って、AWSのELBボリュームがアタッチされているEC2インスタンス名を出力するワンライナーを書いた - 双六工場日誌」のスクリプトの出力は以下のようになります。

i-ec56a9f5 vol-07d00601 servername

i-ec56a9f5 vol-8f550991 servername

このようなレコードの特定の列を取り出して、処理する際にどうするのが効率的か、というのがこのエントリのお題です。

非常に古い話題なので、昔からシェルスクリプトを書いている人には自明な話ではありますが、最近、シェルの標準機能の話を聞く機会がなく、失われつつある技術になってきている気がしているので、改めて確認ということで。

例として挙げたレコードから最初の1列目(インスタンスID)だけ取り出したい時、たとえばこんな方法があります。

line="i-ec56a9f5 vol-07d00601 servername"

instance_id=`echo $line | cut -d' ' -f1`
volome_id=`echo $line | cut -d' ' -f2`
instance_name=`echo $line | cut -d' ' -f3`

こうするとフィールドの切り出しごとに新しく"cut"のプロセスが起動されるため、ワンライナーや実行回数の少ないバッチ処理用のスクリプトの場合はいいのですが、監視スクリプトのような一定の頻度で呼び出されるものは、その差がボディーブローのように効いてくることがあります。

このようにプロセスフォークのコストが無視できない場合は(下線部追記)、シェルスクリプト内で各フィールドの値を使う場合は以下のように"set"で分割しましょう。

line="i-ec56a9f5 vol-07d00601 servername"

set -- $line
instance_id=$1
volome_id=$2
instance_name=$3

ここで使っている$1、$2、$3といった変数は"the positional parameters"と言われるもので、シェルスクリプトでは、スクリプトや関数の引数を参照する際に使っていることが多いと思います。この変数は"set --"のあとに、スクリプトの引数のようなイメージで、空白区切りの文字列を入れるとシェル内で再設定することができます。(シェルスクリプトの引数として与えていた値は消去されます)

この2つで、どれだけの差が出るのか、以下のような簡単なベンチマークを取ってみました。実行環境は、僕のMBA 2012です。

続きを読む