#navi_header|技術| bashでのシェルスクリプトの情報源: - "Manpage of BASH" -- http://www.linux.or.jp/JM/html/GNU_bash/man1/bash.1.html - "シェル・スクリプト・リファレンス INDEX:ITpro" -- http://itpro.nikkeibp.co.jp/article/COLUMN/20060224/230580/ "PS1"等のプロンプトに使えるエスケープシーケンスも、上記manページ中に解説があります。 他、/etc/rc.d/ 以下の各種サービス起動スクリプトが参考になります。 #more|| 以下は実際にシェルスクリプトを書いていた時のTipsです。 ---- #outline|| ---- * "&&" と "||" と "exec" の使い分け ・"&&"の例 : /foo/bar が存在すれば、$HOME/bin/baz を実行。 [ -f /foo/bar ] && $HOME/bin/baz ・"||"の例 : /foo/bar が存在 ''しなければ''、$HOME/bin/baz を実行。 [ -f /hoge/bohe ] || $HOME/bin/uhyou 多くのプログラミング言語もそうですが、or演算の場合、先にtrue評価の式があると、後ろの式は評価されなくなります。bashの "&&", "||" でも同様の動きになります。 ・execの注意点 : execシェルコマンドは ''プロセスを置き換えてしまいます。'' つまりexecが実行されたらそれ以降のシェルスクリプトは実行されません(=復帰しません)。各種スクリプトで "&&", "||" と共に exec コマンドも頻出しますが、この点を読み違え無いよう注意が必要です。 * ワイルドカード(&void()*)の注意点 便利なワイルドカード、&void()*ですが、使用上の注意点もあります。 たとえば、rhostsファイルのあるディレクトリで $ find /etc -name r* として、「rで始まるファイルを/etcから探そう」としても、r* が rhosts に展開されてしまい、 $ find /etc -name rhosts が実行されてしまいます。 これを避けるには、メタ文字展開の抑制を使います。 $ find /etc -name "r*" - シングルクォテーション「'」で囲むと、「!」以外のメタ文字を展開しません。 - ダブルクォテーション「"」で囲むと、「!, $, `」以外のメタ文字を展開しません。 - \(バックスラッシュ)だと、この直後の一文字を展開しません。 メタ文字とは関係が薄いですが、コマンド履歴を呼び出す "!!", "!N" をバッククォートで囲むと、中のコマンドが実行された結果に展開されます。 * trap組み込みコマンドによるシグナルハンドリング '' trap '' 組み込みコマンドを使うと、シェルスクリプトでシグナルハンドラを使うことが出来ます。 trap コマンド シグナル名(or 番号) シグナル名 or 番号は signal.h 定義のものになります。"kill -l"で調べることができます。 コマンドの部分は、シェルの内部コマンド、外部コマンドの両方とも可。 複数のコマンドを指定するには「;」で区切り、コマンド全体を""で囲みます。 特定のシグナルを無視するには、コマンドを空文字列にします。 trap "" シグナル番号 特定のシグナルを初期設定に戻すには、コマンドを省略します。 trap シグナル番号 whileループと組み合わせたサンプル : #pre||> #!/bin/sh echo "PID=$$" while true do trap "echo loop broken...; break" 1 trap "echo loop continues...; continue" 2 date sleep 3 done echo "Good Bye." ||< 実行結果: #pre||> $ ./test.sh PID=1437 2003年 4月 18日 金曜日 00:57:18 JST # 別ターミナルより "kill -2 1437" loop continues... 2003年 4月 18日 金曜日 00:57:27 JST # 別ターミナルより "kill -1 1437" loop broken... Good Bye. ||< * evalでshiftによる引数廃棄を回避してみる。 配列を頭から処理する shift コマンドは、特にコマンドライン引数の処理で良く使われます。 たまに問題になるのが、shiftコマンドだと一旦shiftされた引数が廃棄されてしまう点です。 これをevalコマンドを用いて回避するサンプルコードを作ってみました。 #pre||> #!/bin/bash num=1 while [ $num -le $# ] ; do eval echo "Argument $num is " \$\{$num\} num=`expr $num + 1` done ||< evalは変数を展開しますので、 \$\{$num\} の部分の $num がwhileループで回るnumに展開されます。つまり、例えば最初のループでは echo "Argument 1 is " ${1} が評価(つまり実行)されることになります。 実行例 : $ sh foo.sh a b c d Argument 1 is a Argument 2 is b Argument 3 is c Argument 4 is d * bashのバージョン2以降は配列を扱えます a[0]=a a[1]=b a[2]=c 或いは a=(a b c d) files=(`ls`) 参照するには ... echo ${a[0]} echo ${files[3]} echo ${a[@]} # 全参照 * コロンの変則的な使い方。 ${name:+value} # name変数が空白でなければ、valueが値となる。 ${name:-value} # name変数が空白であれば、valueが値となる。 ${name:=value} # name変数が空白であれば、name=valueとなる。 ${name:?value} # name変数が空白であれば、valueを標準エラー出力に流し、 スクリプトの実行を中止する。 ${name:start} # nameのstartバイト目から最後まで出力。 ${name:start:length} # nameのstartバイト目からlength文字分出力 * case文の条件式部分のバリエーション case $x in y|m) flag="*" ;; n) flag=" " ;; esac これだとy or mの時、が簡単に表現できる。 * 履歴参照 & 履歴からコマンドラインの引数を取ってくる 直前のコマンド実行 !! "ls"で始まる直近のコマンド実行 !ls 直前に実行したコマンドライン中の"foo"を"bar"に置換して実行 !:s/foo/bar 直前のコマンドの最初の引数 vim foo.txt mv !^ 直前のコマンドの最後の引数 hoge.sh a.txt b.txt c.txt rm !$ 直前のコマンドの引数全て hoge.sh a.txt b.txt c.txt rm !* 参考: - bash/zshのコマンドライン履歴機能。直前のコマンド、直前の引数。 - ミームの死骸を待ちながら -- http://d.hatena.ne.jp/Hash/20090927/commandlinehistory - bash(1) : 「イベント指示子 (Event Designator)」、「単語指示子 (Word Designators)」 #navi_footer|技術|