シェルそのものもインタプリタであり,制御構造や変数を持つ。
変数定義は
変数=値
の形式で行なう。イコールの前後に空白は許されない。 シェル変数は慣習的に小文字。定義したシェルのみが持つ。
定義したシェル変数の値の参照は
$変数 ${変数}
いずれかで行なう。単語区切りが明確なときのみ { }
は不要。
ファイル名に対するパターンマッチングをグロッビングという。 正規表現とは規則が違う。
?
任意の1字にマッチ
*
0字以上の任意の文字列にマッチ
[文字クラス]
文字クラス のいずれかに1字にマッチ。
zsh拡張として次のパターンも使える。
**/
ディレクトリを再帰的に検索
<整数1-整数2>
数値的に整数1以上
整数2以下の文字列にマッチする。
上限のみ,下限のみでもよい。たとえば,カレントディレクトリに
15個のファイル,file1.rb file2.rb ... file15.rb
がある場合,
file<4-12>.rb
は,file4.rb file5.rb ... file12.rb
の9個のファイル
file<-9>.rb
は,file1.rb file2.rb ... file9.rb
の9個のファイル
file<8->.rb
は,file8.rb file9.rb ... file15.rb
の8個のファイル
にマッチする。
もちろん存在しないファイル名は出てこない。
パターン1~パターン2
パターン1にマッチするものから,
パターン2にマッチするものを除外する。
たとえば,上述の file1.rb file2.rb ... file15.rb
の15個のファイルがある場合,
file??.*~*15*
と指定すると,"file"
の後ろに任意の2字が来て,
そのあとピリオドと任意文字列が来るファイル名すべてをまず選び,
そこから途中に "15"
という文字列が現れるものを
除外するので file10.rb file11.rb file12.rb file13.rb
file14.rb
がマッチする。
`cmdline`
(バッククォート)
Rubyのバッククォート 同様,内部のコマンド cmdline を起動した結果の文字列に置換される。
$(cmdline)
バッククォートと同様,コマンドを起動した結果の文字列に 置換される。バッククォートと違いネスト可能。
自分が差出人のメイルから,送信先となっている頻度が 一番多い人にメイルを送る手順を例に示す。
cd ~/Mail/inbox grep -l "Return-Path:.*$USER" * (マッチするファイル名一覧が出る) grep '^To:' $(grep -l "Return-Path.*$USER" *) (自分が差出人のファイルから To: ヘッダの行を抽出) grep '^To:' $(grep -l "Return-Path.*$USER" *) | \ ruby -pe 'sub(/.*[ <](.+@[^>]*)>?/,"\\1")' (送信先のアドレス一覧が出る) grep '^To:' $(grep -l "Return-Path.*$USER" *) | \ ruby -pe 'sub(/.*[ <](.+@[^>]*)>?/,"\\1")' | \ sort | uniq -c | sort -nr | head -1 | awk '{print $2}' (頻度1位の人のアドレスが出る) echo 1位です! | nkf -j | \ Mail -s congratulations $(grep '^To:' $(grep -l "Return-Path.*$USER" *) | \ ruby -pe 'sub(/.*[ <](.+@[^>]*)>?/,"\\1")' | \ sort | uniq -c | sort -nr | head -1 | awk '{print $2}')
中括弧 { }
の内部にカンマで区切った文字列を列挙すると
それらを開いた文字列に展開する。前後に文字列をつけると
それらと結合した上で展開する。
echo {a,b,c} a b c echo file-{a,b,c} file-a file-b file-c echo hoge.rb{,bak} hoge.rb hoge.rb.bak echo cp Ruby/kadai1/hogehoge.rb{,.bak} cp Ruby/kadai1/hogehoge.rb Ruby/kadai1/hogehoge.rb.bak
zsh拡張として数値範囲を指定した展開ができる。
echo {1..20} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 echo file{1..20} file1 file2 file3 file4 file5 file6 file7 file8 file9 file10 file11 file12 file13 file14 file15 file16 file17 file18 file19 file20 (空のファイルを作るためにtouchコマンドを用いる) touch file{1..999} (lsで確認後消去) ls rm file{1..999}
あるコマンドの実行が成功したとき(終了値0のとき),失敗したとき で分岐したり,繰り返し処理を行なったりできる。
cmdline1 && cmdline2
cmdline1 の実行が成功したときのみ cmdline2 を実行する。
cd touch myprogram.rb && chmod +x myprogram.rb (ホームディレクトリには作れるので chmod +x myprogram.rb される) rm myprogram.rb touch /myprogram.rb && chmod +x /myprogram.rb (ルートディレクトリには作れないので chmod されない)
cmdline1 || cmdline2
cmdline1 の実行が失敗したときのみ cmdline2 を実行する。
if cmdline1; then 〜 elif
cmdline2; then 〜 else 〜 fi
cmdline1 の実行が成功したときに
then
に続くブロックを評価する。
いずれのコマンドも失敗したときは
else
ブロックを評価する。
#!/bin/sh if touch hogehoge.rb then echo ./hogehoge.rb 作れました elif touch /tmp/hogehoge.rb echo /tmp/hogehoge.rb 作れました else echo 失敗しました。やめ。 exit fi
コマンドの部分には条件判断を行なうだけのコマンドを
使うことが多い。典型的には /bin/test
コマンドで
これはファイルの存在を確かめたりできる。Rubyの
test関数は
外部コマンド test
に由来する。
#!/bin/sh if /bin/test -f hogehoge.rb then echo hogehoge.rb ファイル,あります。 else echo hogehoge.rb ファイル,ありません。 fi
条件式に見えやすいよう,test
コマンドには
[
という名前のハードリンクが張られているので,
これを用いて書き換えると以下のようになる。
#!/bin/sh if [ -f hogehoge.rb ]; then echo hogehoge.rb ファイル,あります。 else echo hogehoge.rb ファイル,ありません。 fi
test
の代わりに [
で
呼び出したときは行の終わりに ]
を付ける。
while cmdline; do 〜; done
cmdline が成功を返す間ブロックを評価し続ける。
cmdline の部分には test
コマンドや,
常に成功する /bin/true
コマンドを用いることが多い。
#!/bin/sh while true; do echo 誰か止めて〜 sleep 1 done
for 変数 in 語群; do 〜 ; done
語群 (を展開した結果)を先頭要素から順に 変数 に代入しつつブロックを繰り返す。
次の例はカレントディレクトリに含まれる *.rb
ファイルすべてのバックアップファイルを作成する。
for f in *.rb; do
cp $f $f.bak
done
シェルは標準入出力を処理するフィルタとしても振る舞える。
シェルが直接標準入力を読むには read
、
標準出力に送るには echo
を用いる。
read 変数 ...
標準入力から1行読み込み、変数 に代入する。 変数を2個以上指定した場合は空白文字でフィールド分割した 各フィールドを先頭の変数から順次代入する。フィールドの方が 多い場合は最後の変数に残りすべてが代入される。
read x foo bar baz x -> "foo bar baz" read x y foo bar baz x -> "foo" y -> "bar baz" read x y z foo bar baz x -> "foo" y -> "bar" z -> "baz"
フィールド区切りを空白以外に変更するにはシェル変数
IFS
に区切り文字を列挙する。CSVファイルを
読み取って処理する例を示す。
山田太郎,50,70,20 公益太郎,90,80,70 飯森花子,91,79,72 鶴岡一人,60,60,40 酒田三吉,52,70,80 三川一二三,12,34,99
cat score.csv | while IFS=, read name math eng jp
do
sum=`expr $math + $eng + $jp`
echo $name さんは合計 $sum 点です。
if [ $sum -lt 200 ]; then
echo "*** $name さんは赤点です!! ***"
fi
done