第7章 簡単なシェルスクリプトを書いてみよう
もし何度も繰り返すタスクがあって、そのたびに大量のコマンドを打ち込まないといけないなら、シェルスクリプトを書いて一つのコマンドで全部済ませちゃうのがいいよ。
7.1 シェルスクリプトを理解する
シェルスクリプトは、コマンド、関数、変数、その他シェルで使える機能をひとまとめにしたもの。これらをプレーンテキストファイルに書き込むことで、一つのコマンドとして実行できるようになるんだ。
Windowsのバッチファイル (.bat) みたいなものだね。
7.1.1 シェルスクリプトの実行とデバッグ
シェルスクリプトのいいところは、どんなテキストエディタでも開いて中身を確認できること。逆に欠点は、大きくて複雑なスクリプトだと、コンパイル済みのプログラムより実行が遅くなりがちなことかな。実行方法は基本的に2つあるよ。
スクリプト名を bash の引数として渡す。例:
bash myscriptシェルスクリプトの1行目にインタプリタの名前 (
#!/bin/bash) を書いて、そのファイルに実行権限を付けてから (chmod +x myscript)、コマンドラインでスクリプトのパスを入力して実行する。例:(./myscript.sh)
実行するとき、スクリプト名の後に続くのがコマンドライン引数だよ。
コメントは
#を使う。スクリプトの最初に
set -xを入れるか、$ bash -x myscriptと打つことで、実行中のコマンドを表示しながらデバッグできるよ。
7.1.2 シェル変数を理解する
シェル変数の名前はは大文字と小文字を区別するから注意してね。定義するときは等号 (=) の前後にスペースを入れないのがルールだよ。例:
NAME=value
変数にはテキスト、数字、アンダースコアなどの定数を代入できる。
コマンドの結果を変数に入れることもできるよ。例:MYDATE=$(date) と書くと、date コマンドの出力が変数 MYDATE に入るんだ。
こうすると、変数 MYDATE を使うたびに一回 date コマンドが動いて、その結果が MYDATE に代入される。代入時の実行結果が欲しいときは、コマンドを引用符 ' で囲めばOK。
特殊なシェル文字:ドル記号 ($)、引用符 (')、アスタリスク (*)、感嘆符 (!) など。
もしコマンドラインで $HOME という文字列をそのまま表示したいなら、$ をエスケープする必要がある。echo '$HOME' か echo \$HOME を使えばいいよ。つまり:
シェルに特定の文字をそのまま解釈させたいときは、バックスラッシュ
\を使う。文字の集まりをそのまま解釈させたいときは、シングルクォート (
') で囲む。一部の文字だけそのまま解釈させたいときは、ダブルクォート (
") で囲む。この場合、ドル記号 ($)、引用符 (')、感嘆符 (!) は解釈されるけど、アスタリスク (*) などの他の文字はそのまま扱われるよ。
変数に値を代入するときはそのまま名前を使うけど、変数を参照(値を取得)するときは頭にドル記号 (
$) をつけるのを忘れないで。例:ある変数の値を新しい変数に入れる:
newVar="$oldVar"
特殊なシェル位置パラメータ
位置パラメータ、つまり コマンドライン引数 は、$0、$1、$2…$n という名前になっているよ。
$0 は呼び出されたスクリプト自身の名前で、それ以外はコマンドラインから渡された引数の値が入るんだ。例えば:
| |
./myscript hello bye というコマンドを実行すると、結果はこうなる:
| |
もう一つ面白いのが $? で、これは最後に実行されたコマンドの終了ステータスを受け取る。普通、正常に終われば 0 が返ってくるよ。
引数を読み取る
read コマンドを使うと、ユーザーの入力を受け取ることができる。
| |
- Bash でのパラメータ展開
変数の値が欲しいときは頭にドル記号 ($) をつけて $var と書くけど、これは実は ${var} の短縮形なんだ。
Bash にはパラメータの値をいろいろな方法で展開するルールがある。よく使うものを ${var} を例に挙げておくね。
| 例 | 説明 |
|---|---|
| ${var:-value} | もし変数が未設定か空なら、value として展開する |
| ${var#pattern} | var の値の 前方 から、pattern と 最短 で一致する部分を削る |
| ${var##pattern} | var の値の 前方 から、pattern と 最長 で一致する部分を削る |
| ${var%pattern} | var の値の 末尾 から、pattern と 最短 で一致する部分を削る |
| ${var%%pattern} | var の値の 末尾 から、pattern と 最長 で一致する部分を削る |
これを使うと、こんな便利なことができるよ:
| |
7.1.3 シェルスクリプトで計算をする
Bash の変数は型が決まっていない(非型指定)。declare で明示しない限り、変数は文字列として扱われるんだ。でも計算するときは自動的に整数に変換されるから、代入時に型を気にする必要はないよ。
内蔵の let コマンド、外部の expr コマンド、あるいは bc コマンドを使って整数の計算ができる。
例:let result=$num/16 や let num=$RANDOM
インクリメント演算子の i++ や ++i も使えるよ。
letコマンドは、演算子と数字の間にスペースを入れちゃダメ。
exprコマンドは逆に、演算子と数字の間にスペースが必要。
bcコマンドはスペースを気にしなくていいし、浮動小数点(小数)の計算もできるよ。
7.1.4 シェルスクリプトでプログラミング構造を使う
- “if…then” 文
| |
数字を比べるなら -eq がいいけど、文字列を比べるなら等号 (=) のほうが使いやすいよ。
| |
不等号 != もある。
elif を使えば選択肢を増やせるし、else で「それ以外」の場合を指定できる。
| |
よく使うテスト条件のリストだよ:
| 演算子 | テストの内容 |
|---|---|
| -a file | ファイルが存在するか(-e と同じ) |
| -b file | ブロックデバイスファイルか |
| -c file | キャラクタデバイスファイルか |
| -d file | ディレクトリか |
| -e file | ファイルが存在するか(-a と同じ) |
| -f file | 普通のファイルか(ディレクトリやデバイスファイルじゃないか) |
| -g file | SGID ビットがセットされているか |
| -h file | シンボリックリンクか(-L と同じ) |
| -k file | スティッキービットがセットされているか |
| -L file | シンボリックリンクか(-h と同じ) |
| -n string | 文字列の長さが 0 より大きいか |
| -O file | 自分がそのファイルの所有者か |
| -p file | 名前付きパイプか |
| -r file | 読み取り可能か |
| -s file | ファイルが存在して、サイズが 0 より大きいか |
| -S file | ソケットファイルか |
| -t file | ターミナルに接続されたディスクリプタか |
| -u file | SUID ビットがセットされているか |
| -w file | 書き込み可能か |
| -x file | 実行可能か |
| -z string | 文字列の長さが 0 か |
こっちは2つの変数を比べるとき:
| 演算子 | テストの内容 |
|---|---|
| expr1 -a expr2 | 両方の式が真か |
| expr1 -o expr2 | どちらかの式が真か |
| file1 -nt file2 | ファイル1のほうが新しいか(タイムスタンプで比較) |
| file1 -ot file2 | ファイル1のほうが古いか(タイムスタンプで比較) |
| file1 -ef file2 | 2つのファイルが同じ実体(ハードリンクかシンボリックリンク)か |
| var1 = var2 | 2つの変数が等しいか |
| var1 -eq var2 | 2つの変数が等しいか |
| var1 -ge var2 | var1 が var2 以上か |
| var1 -gt var2 | var1 が var2 より大きいか |
| var1 -le var2 | var1 が var2 以下か |
| var1 -lt var2 | var1 が var2 より小さいか |
| var1 != var2 | 2つの変数が等しくないか |
| var1 -ne var2 | 2つの変数が等しくないか |
さらに、テスト演算子を &&(AND)や ||(OR)と組み合わせて、C言語の三項演算子みたいに書くこともできるよ。
C言語:a>b ? a : b
Shell:[ $a -gt $b ] && echo $a || echo $b
単独でも使える。例えば:
[ $a -eq $b ] && echo $a は「a と b が等しければ、a を表示する」。
[ -d "$dirName" ] || mkdir "$dirName" は「$dirName というディレクトリがなければ、mkdir で作る」。
- case コマンド
C言語の switch 文みたいなもので、条件分岐に使うよ。基本の形はこんな感じ:
| |
- for…do ループ
for ループはリストの中身を順番に処理するときに使うよ。
| |
例えば:
| |
- while…do と until…do ループ
構造はこんな感じ:
| |
7.1.5 便利なテキスト操作プログラムを使う
よく使われるのは grep, cut, tr, awk, sed。ほとんどが標準入力と標準出力を使うように設計されているよ。
- 一般正規表現アナライザー (grep)
ファイルやテキストの中から特定のパターンを探す方法。強力な検索ツールとして使えるよ。
形式:grep 探したい内容 入力元
詳しくは man grep で確認してみて。
- テキストの切り出し (cut)
cut コマンドはテキストやファイルから特定のフィールドを抜き出すことができる。例えば:
grep /home /etc/passwd | cut -d':' -f6 -
まず grep で /etc/passwd から /home を含む行を探して、それを cut に渡す。cut は : を区切り文字として使って、6番目のセグメント (-f6) を取り出しているよ。
- 文字の変換や削除 (tr)
tr は文字ベースのコンバーターで、文字を置換したり削除したりできる。
| |
- ストリームエディタ (sed)
sed は簡単なスクリプトエディタ。特定のパターンに一致する行を消したり、文字を置換したりといった編集ができる。
かなり奥が深いから、オンラインドキュメントで調べてみてね。
7.1.6 簡単なシェルスクリプトを使ってみる
電話リストの例だよ:
| |
7.2 小結
シェルスクリプトを書けるようになると、よくあるシステム管理タスクを自動化できるようになって、すごく便利になるよ。