May 13, 2008

grep & awk

grep "^HOGEHOGE" file | awk 'BEGIN { FS = "="}; { print $2 }'
というコード。一見何も問題ない様に見えるし確かに動作はするが、 CPU とプロセスと時間の無駄遣いである。

awk (1) には拡張正規表現が実装されているで、

awk 'BEGIN{FS="="}/^HOGEHOGE/{ print $2 }' file
とすれば grep (1) の分だけ CPU やプロセス、時間が節約できる。

sed -n '/^HOGEHOGE/s/^.*=//p' file
とするのも等価かな。
行指向の処理であれば煩雑な awk (1) よりも sed (1) の方が見やすいので、 個人的には sed (1) を利用した方法がお勧め。

シェルスクリプトに限らずスクリプト言語で記述されたプログラムは、 source file が直接人の目に触れる可能性が高いので、 恥ずかしくないプログラミングを心がけたいと思う。

  • export (1) しないシェル変数は全て小文字で書く。
  • [ -z "${foo}" ] && foo="FOO"
    
    という書き方でも問題ないけど、
    ${foo:-FOO}
    
    と記述した方がすっきり記述できるし意味が判りやすいのでお勧め。
  • trap (1) で 0 を指定するとスクリプトが正常終了した時にも 指定した処理が実行されるので、
    プログラム中で作成した一時ファイルの削除などにはもってこい。
  • grep (1) や cut (1) と awk (1) や sed (1) を組み合わせて使うのは、 殆どの場合において CPU とプロセスと時間の無駄遣い。
    あまりにも複雑な正規表現が必要な場合や 処理が煩雑になる場合は勿論この限りではない。
  • エラーメッセージは標準エラー出力に出そう。 unix を使う上では基本中の基本です。
  • インデント幅はスクリプト内で統一しましょう。 Tab でも スペース何個でも OK ですが、 1ファイルの中のインデント位は合わせないと恥ずかしいです。
  • ${PATH} は信用せずにフルパスをシェル変数に代入して使う様にしましょう。
    たまに ${PATH} に "." を含める脳障害としか思えないアホがいるので、 スクリプト中で使用するコマンドは
    foo="/path/to/foo"
    :
    ${foo}
    
    な感じで使うが良いと思います。
    foo="echo /path/to/foo"
    
    スクリプトのデバッグ中はこんな感じに変更しとくと mv (1) や rm (1) の様なコマンドの実行を抑止できる。
    logger="/usr/bin/logger -p daemon.err -t ${myname}"
    
    なんて設定しとくと共通のロギング処理にもなるので安全便利。
    linux 環境だと /bin/sh が bash だったりするので、 設定によっては cp や rm などが、 cp -i とか rm -i などの様な迷惑千万な alias に設定されていても スクリプトの動作に支障をきたさない。
    シェルの内部コマンドも含め全てのコマンドは execv (2) 可能でなければならないと POSIX で決まっているので、 POSIX 準拠を詠っている OS であれば全てのコマンドが揃ってる(筈)。
  • 複数行に渡る長いメッセージや固定データは echo (1) ではなく てヒアドキュメントを。
    echo "………" > ${output}
    echo "………" >> ${output}
    echo "………" >> ${output}
    
    などと書いていると途中行を修正する時に ">>" をついうっかり ">" にしてしまったりする間違いをする危険性が高いので、
    cat << EOF> ${output}
    ………
    ………
    ………
    EOF
    
    と記述する様にすれば安心確実です。
  • スペースで区切られたデータの構文解析は set (1) を活用しよう。
    年、月、日 をそれぞれ $year、$month、$date に代入する処理は素直に書けば
    date=`date '+%Y %m %d'`
    year=`echo $date | awk '{ print $1 }'`
    month=`echo $date | awk '{ print $2 }'`
    day=`echo $date | awk '{ print $3 }'`
    
    な感じになるが date (1)、echo (1)、 awk (1) の呼び出しが煩雑になる。
    それならば
    set `date '+%Y %m %d'`
    year=$1
    month=$2
    day=$3
    
    の方が CPU やプロセス、時間の節約になる。
    勿論位置パラメタは上書きされるので必要な値は事前にシェル変数に保存する。
  • bash や zsh に依存した処理は絶対使わない
    勿論 csh スクリプトなんて論外。
Edit this entry...

wikieditish message: Ready to edit this entry.
















A quick preview will be rendered here when you click "Preview" button.