################################################################ # Bourne sh メモ ################################################################ ■特に注意するべきこと - ファイル名展開が出来ない場合の動作 *, ?, [] などでファイル名への置換が出来ない場合には, これらの文字そのものが使用される - 代入の = の前後に空白を入れてはならない - { の後ろには空白が必要 - 数値演算には expr コマンドを使う - 条件判定には test コマンドを使う 別形式は [ 条件 ] になる。[] のすぐ内側には空白が要る - if 文の then はコマンドとし認識される位置になければならない if [ -f $1 ]; then # if と同じ行に書くには必ず ; が必要 ################################################################ ■シェルがコマンドラインを解析する手順 1. コマンドラインから特殊文字*, ?, [, ]を探す * NULL 文字列を含む,任意の文字列 ? 任意の単一文字 [..] 囲まれた文字のいずれか [1sz] [A-Z] という書き方もあり 2. 文字が見つかればファイル名で置換を試みる。置換できなければそのまま 3. コマンドラインから起動すべきコマンドと引数を取り出しコマンドを起動する ■リダイレクション ディスクリプタ 0: 標準入力 1: 標準出力 2: 標準エラー出力 [n]< file ディスクリプタ n (省略時 0) でfileを読み込み用にオープン [n]> file ディスクリプタ n (省略時 1) でfileを書き込み用にオープン [n]>> file ディスクリプタ n (省略時 1) でfileを追加書き込み用オープン << 'eof' 標準入力を次行から eof の直前行までとする(here document) n>&m ディスクリプタ n の出力をディスクリプタ m に変更 n<&m ディスクリプタ m の入力をディスクリプタ n に変更 [n]<&- 入力ディスクリプタ n (省略時 0)をクローズ [n]>&- 出力ディスクリプタ n (省略時 1)をクローズ [ ]内は省略可。ディスクリプタはオープンしたファイル番号で0〜9の数字で表す 【例】 $ wc < foo $ wc foo > hoge $ wc foo 2> err.txt (標準エラー出力をリダイレクト) $ wc foo 2>- (標準エラー出力をクローズ) $ wc foo 1> output.txt 2>&1 (エラーメッセージも同じファイルに書き出す) スクリプトの中で.. cat <<'EndOfFile' > data.txt JAL 123 NGY NYI ANA 465 OSK CHT NWA 453 LND LAA EndOfFile ■パイプとバッググラウンドタスク | と & (csh と同じ) ■コマンドの区切り文字 ; ';' で区切ることによって複数のコマンドを書くことができる ■グルーピング (cd /bin; ls -C) # サブシェルで実行 { cd /bin; ls -C; } # カレントシェルで実行 ※ { の直後と,}の直前には 1 個のスペースが必要 ※ { } によるグルーピングの最後のコマンドの後ろには ';' が必要 ■環境,環境変数 PS1, PS2 プロンプト文字列 (PS2 はコマンド行が複数にまたがる場合に使われる) .profile ログイン時に実行されるスクリプトファイル export $var 変数 var を環境変数に設定する ■ドットコマンド $ . script .profile を実行してからスクリプトを実行する スクリプトに executable ビットが立っている必要はない ■シェル変数 var=$value 代入には '=' を使う '='の前後に空白を入れてはいけない! 変数を参照するには変数名の頭に '$' をつける ${var}_item 変数名の明示的な区切りには '{' '}' を使う readonly var 変数 var を読み取り専用にする このとき変数名の前の '$' はつけない var=value command 変数 var に値 value を代入した状態でコマンドを実 行する(サブシェルでコマンドが実行されたときに 変数 var に値が入った状態になる) ■引用符 '' シングルクォートは,変数置換や展開をまったくしない バックスラッシュさえ無視される シングルクォートの中で,シングルクォートを含めることはできない "" ダブルクォートの中では $var, `` (バッククォート), バックスラッシュが認識される。 バックスラッシュはエスケープ文字として働く 変数展開が行われる バッククォートによるコマンド実行を行う `` バッククォートは,これで囲んだコマンドの実行結果を文字列として返す 【例】$ echo "Now is `date`" ■特殊変数 $IFS フィールド区切り文字(デフォルトはホワイトスペース) $# コマンドラインに与えられた引数の個数 (コマンドそのものは含まない。つまり引数がなければ値は 0) $1 〜 $9 それぞれ n 番めの引数 (定位置バラメータ) shift を使って,$1 から取り出せる。 shift を使うと,値は前にずれていき,$# も 1 ずつ減って行く。 shift に引数 n をつけると,一度に n 回 shift する $0 実行したスクリプトの名前 $* $0 以外のすべての引数に対応する $@ $0 以外のすべての引数に対応する * $* と $@ の違い ダブルクォートで囲んだ場合に違いがある 引数が 'abc def' と xyz のとき "$*" 'abc' 'def' 'xyz' "$@" 'abc def' 'xyz' としてコマンドに渡される $? sh が最後に時効したコマンドの終了状態 $$ sh の PID $- セットされている sh のオプション ■ set コマンド 二つの機能を持つ ・シェルのオプションの設定と解除 set -a a フラグを ON にする set +a a フラグを OFF にする ・引数の文字列(レコード)から $IFS で区切られたフィールドに切 り出し,位置パラメータに入れる set a b c 文字列 a, b, c がそれぞれ $1 $2 $3 にはいる 引数無しで set を実行すると全変数をリストアップする (csh とは異なり,環境変数もリストアップする) ■ unset コマンド シェル変数,環境変数の削除 ======================================================================= 構文規則 ======================================================================= ■定義 単純コマンド: ブランクで区切られたブランクでないワードの並び 先頭のワードは実行すべきコマンド名 パイプライン: パイプ (|) で区切られた 1 つ以上のコマンドの並び リスト: ; & && または || で区切られた 1 つ以上のパイプライ ンの並び。 並びの後ろに ; または & があっても良い リスト中のコマンドを区切るのに ; の変わりに 改行を使うことも可能 各記号類の優先度 1. | 2. && || 3. ; & コマンド: 単純コマンドまたは以下のいずれか ・[] は省略可能なことをを意味する ・以下のワードはコマンドの先頭かつクォート去れてい ない時のみ認識される if then else elif fi case esac for while until do done { } ■ 条件分岐 case ワード in パターン [ | パターン] ) リスト ;; ... esac if リスト ; then リスト ; [ elif リスト ; then リスト ; ] [ else リスト ; ] fi リストの最後のコマンドの終了ステイタスで判定 ■ ループ for 変数名 [ in ワードのリスト ] do リスト done (Perl での foreach) while リスト do リスト done until リスト do リスト done リストの最後のコマンドの終了ステイタスで判定 ■ グルーピング (リスト) サブシェル内でのリストの実行 { リスト:} 現在のシェル内でのリストの実行 { の後ろには空白が必要 ■ 関数定義 関数名 () { リスト;} ・リストが関数の本体 ・{ の後ろには空白が必要 ・関数の本体が単一のコマンドである場合, 前後の {} は不要 ======================================================================= ■構文の例(制御構造) ### 普通に書いた場合 case $1 in a) echo a echo A ;; b) echo B ;; esac if [ $1 = $2 ]; then echo $1 and $2 echo is same else echo $1 and $2 echo is different fi for a in *.txt *.sh do printf "%s\t" $a cat $a | wc -l done a=0 while [ $a -le 10 ] do echo [$a] a=`expr $a + 1` done ### なるべく短く書いた場合 case $1 in a)echo a; echo A;;b)echo B;;esac if [ $1 = $2 ];then echo $1 and $2;echo is same;else echo $1 and $2;echo is different;fi for a in *.txt *.sh; do printf "%s\t" $a;cat $a | wc -l;done a=0;while [ $a -le 10 ];do echo [$a];a=`expr $a + 1`;done ■制御構造に関連するもの : コマンド 何もしない。終了ステイタスは常に 0 break for または while (until) ループから抜け出す 引数 n があると,n レベル分抜け出す。 continue for または while (until) ループの次のイテレーションを実 行する。引数 n があると n 番目のループから実行する。 command1 && command2 command1 の終了ステータスが真なら command2 を実行 if command1; then command2; fi と等価 command1 || command2 command1 の終了ステータスが偽なら command2 を実行 if command1; then :; command2; fi と等価 ■ループ文のリダイレクト,パイプ,バックグラウンドでの実行 - ループ全体に対してリダイレクトが可能 for i in Text Book; do echo $i; done > output.txt while read line; echo $line; done < input.txt - ループ全体をパイプに繋げることができる for i in Text Book; do echo $i; done > grep 'B' - ループ全体をバックグラウンドで実行可能 for i in Text Book; do echo $i; done > output.txt & *注意: リダイレクト,パイプを使う場合,ループの中身はサブシェ ルで実行される。つまり,ループ内で変更された変数はルー プの外ではその変更が反映されない ■条件判定: test コマンド 書式: test condition または [ condition ] [] の内側には空白が必要 演算子の前後には空白が必要 str1 = str2 文字列 str1 と str2 が一致 str1 != str2 文字列 str1 と str2 が不一致 -n str 文字列 str は NULL ではない -z str 文字列 str は NULL int1 -eq int2 整数 int1 と int2 は等しい int1 -ne int2 整数 int1 と int2 は等しくない int1 -gt int2 整数 int1 は int2 より大きい int1 -ge int2 整数 int1 は int2 以上 int1 -lt int2 整数 int1 は int2 より小さい int1 -le int2 整数 int1 は int2 は以下 -d file file はディレクトリ -f file file は通常ファイル -r file file は読み込み可 -w file file は書き込み可 -x file file は実行可 -s file file は 0 バイトではない(中身をもつ) (ファイルテストはこれ以外にもあり) ! expr 論理式 expr の否定 expr1 -a expr2 論理式 expr1 と expr2 論理積 expr1 -o expr2 論理式 expr1 と expr2 論理和 '(' ')' グループ化を表す。 クォートが必須(sh で意味をもつから) ■数値演算: expr コマンド 数値演算での使い方: a=`expr $a + $b` 数値は整数であること。整数以外は文字列と見なされる 論理演算では,非ゼロが真,ゼロが偽とみなされる。sh の中とは逆。 sh 内での論理演算には expr より,test コマンドを使う方が良い。 ( ) 優先順位を明示するのに使う str : regexp 文字列 str と正規表現 regexp を比較 一致した文字数を返す regexp が \( * / % + - 普通の意味を持つ <, <=, = 比較演算,真なら 1 偽なら 0 を返す !=, >, >= val1 & val2 (val1 != 0 && val2 != 0) なら val1 それ以外は 0 を 返す(論理積) val1 | val2 (val1 != 0) なら val1,そうでなければ val2 を返す (論理和) 演算子 ( ) ,*,は sh 上で意味を持つので \ でエスケープして使うよう にする。 ■実行制御 exit [n] 終了状態 n で sh を終了する n が指定されない時は,直前のコマンドの終了状態を sh の終了状態とする exec [argument] 現在の sh の変わりに,引数で指定されたコマンドを新 規のプロセスを生成せずに実行する。 また,引数がない場合でも,これによってシェルの入出 力が変更される。 【例】 exec < file exec 2> file exec foo.exe 2>file eval argument 引数をシェルの入力として解析してからコマンドとして 実行する wait [n] 当該ユーザーのバックグラウンドプロセスのうち ID が n の プロセスを待ち、その終了ステータスを返す。 n が省略され た場合、当該ユーザーの現在活動中のすべてのバッ クグ ラ ウンドプロセスを待ち、リターンコードは 0 になる。 trap コマンド シグナル番号のリスト シグナルを受信した時に行うコマンドを指定する シグナルを無視する場合には trap '' シグナル番号のリスト のように,コマンドを '' でヌル文字列に指定する ■良く使うシグナル 1 SIGHUP 回線切断 2 SIGINT 割り込み 13 SIGPIPE パイプ切断 15 SIGTERM 終了 【具体例】 # 一時ファイルを必ず消す signal 'rm -f $tmpfile; exit 1' 1 2 15 cp $1 $tmpfile # コピー中の割り込みで処理が止まるのを防ぐ signal '' 1 2 15 # シグナルを無視 cp $tmpfile $1 ■ sh の実行オプション(一部) -v 一行読む語とにコマンドを表示。デバッグに威力を発揮。 -x コマンドを実行するたびにそのコマンド名と引数を + に続いて表示す る。変数展開,ファイル名展開の確認に便利