unoh.github.com

シェル(bash)スクリプトを書くときのTips

Mon Sep 01 19:47:37 -0700 2008

尾藤正人(a.k.a BTO)です

UNIXを使う時にはシェルスクリプトをよく使います。
ちょっとしたコマンドを実行するには、シェルスクリプトは非常に便利です。
今回はシェルスクリプトを書くときに覚えておいた方が便利なTipsを紹介します。

非互換性



ここで紹介するものは基本的にbashで動作するものになります。
伝統的なBourne Shellでは動作しないことが多くあると思います。
しかしながら最近はbashがメインで使われることが多いので、"だいだいの環境で動くからおk"ぐらいのノリで使ってもらえればと思います。

$(...)



コマンドを"$(", ")"で囲むと実行結果をコマンドラインに代入してくれます。
一見これは"`"(バッククオート)と同じに見えますが、"$()"にはネストができるという利点があります。

例えばシェルスクリプト自身の絶対パスを取得するのは次のようにできます。

echo $(cd $(dirname $0);pwd)


バッククオートだと一時変数に代入しないとできませんが、"$()"を使うとネストが可能なので、一発で取得する事ができます。
ちなみにこれは、相対パスでも絶対パスでもうまく動作します。

((...)), $((...))



元々シェルは数値演算能力を持たないので、シェルスクリプト内で数値演算を行うには"expr"という外部コマンドを呼び出していました。
最近のシェルだと"((", "))"で囲むことで数値演算を行ってくれるようになってます。
"((...))"を評価した結果がそれぞれ、"0", "0以外"で終了ステータスが "1", "0"になります(ややこしや〜)。
演算結果をコマンドラインに代入したい場合は"$((", "))"で囲めばできます。

"$((...))", "((...))"を使うメリットはいくつかあります。



if ((1)); then    # ((1))は終了ステータスが"0"なので"true"が表示される
  echo true
else
  echo false
fi


$ echo $((1+2))    # 演算子の間にスペースがいらない
3
$ echo $(expr 1+2)   # exprだと演算子の間にスペースが必要
1+2
$ echo $(expr 1 + 2)    # スペースあけるとうまくいく
3
$ echo $((1.1+2.2))    # 浮動小数点もいける
3.3000000000000003
$ echo $(expr 1.1 + 2.2)    # exprだと浮動小数点はダメ
expr: non-numeric argument


read(Borne Shellでも使える?)



findの出力結果をベースに大量のファイルに一気に処理を行う時はどうやるでしょうか。
よくみかけるのが、こんなスクリプトです。

for f in $(find ...); do
  echo $f
  echo $(basename $f)
done


これは"find"の実行結果が長くない時には、うまく動作します。
"find"の実行結果が長いときによく利用するのが"xargs"です。

find ... | xargs command


xargsは僕も大好きなコマンドの1つで、1つのコマンドを複数ファイルに実行するのにとても便利です。
ただ複数のコマンドを実行したい時に不便です。

これは read という内部コマンドを使うとうまく動作します。
read は標準入力から読み込んだデータを引数に指定した変数に代入します。
"find"の出力結果をパイプで流して、"read"を使って順番に変数に代入すると、"find"の実行結果が長くても動作しますし、複数のコマンドの実行も簡単に行えます。

find ... | while read f; do
  echo $f
  echo $(basename $f)
done


まとめ



シェルスクリプトを書く時に覚えておくと便利なTipsを紹介してみました。
他にもいろいろ便利な書き方があるかと思います。
シェルスクリプトは使いこなせるようになると非常に便利です。
みなさんもいろいろ勉強してみてはいかがでしょうか。