bashで始めるシェルスクリプト基礎の基礎:Windowsユーザーに教えるLinuxの常識(8)(1/2 ページ)
GUIに対するCUIの優位性の1つとして、作業の自動化が挙げられる。普段行う作業を1つのコマンドにまとめたり、複数のファイルに対して同じ処理を繰り返し行ったりといったことが比較的簡単なのだ。WindowsにもWSH(Windows Script Host)が用意されている。しかし、Linuxのbashスクリプトの方が簡単なのだ。
シェルスクリプトの基本はコマンドを並べること
コマンドによる作業を自動化するには、その内容を記述したテキストファイルを用意すればいいのです。このテキストファイルを「シェルスクリプト」といいます。
最も簡単な自動化
最も簡単なシェルスクリプトは、コマンドをそのまま並べることです。例えば、tarコマンドでファイルのバックアップを取るとしましょう。単純にルートディレクトリから下を全部バックアップするにしても、/devや/tmpは必要ないですね。すると、
$ tar cf /dev/nst0 /bin $ tar cf /dev/nst0 /boot $ tar cf /dev/nst0 /etc $ tar cf /dev/nst0 /home
といった具合になります(注)。
これをいちいちタイプするのは面倒です。そもそも、バックアップにはある程度の時間がかかるのが普通ですから、前のコマンドが終了するまで次のコマンドはタイプできません。これでは作業効率が上がらないでしょう。
そこで、シェルスクリプトを作ってみましょう。sysbackup.sh(注)という名前のファイルを作り、
tar cf /dev/nst0 /bin tar cf /dev/nst0 /boot tar cf /dev/nst0 /etc tar cf /dev/nst0 /home
上記のように実行するコマンドを並べるだけです。WSHだと、普段メニューとマウスで行っている作業をVB ScriptやJScriptに翻訳しながらプログラムを作る必要があります。ところが、シェルスクリプトは普段使っているコマンドを書くだけでいいのです。
$ file sysbackup.sh
実行するのも簡単です。
$ sh sysbackup.sh
とすれば、自動的に順次コマンドを実行します。その間にほかの仕事ができるし、就寝前に実行すれば、翌朝には終了しているでしょう。
もう少しコマンドらしく
作成したシェルスクリプトを普通のプログラムと同じ方法で実行することもできますが、それには2つばかり細工が必要です。まず、シェルスクリプトの先頭に次の行を追加します。
#!/bin/sh
これはお約束で、「#!」以後に書かれたプログラムでこのスクリプトを実行するという意味です。応用として「#!/bin/perl」「#!/bin/ruby」(編注)などもあります。
もう1つは、ファイルに実行属性を付けることです。chmodコマンドを使って、
$ chmod u+x sysbackup.sh
とします。この2つの作業を行えば、
$ ./sysbackup.sh
で実行できるようになります。
シェルスクリプトによる出力の制御
結果の保存
シェルスクリプトをバイナリの実行ファイルと区別しないのは、入出力のリダイレクトでも同じです。つまり、
$ ./sysbackup.sh > log.txt
とすれば、出力結果をlog.txtというテキストファイルに保存できます。また、定期的に実行するのであれば、スクリプトの中でリダイレクトを指定することもできます。この場合、柔軟性がやや低下する点に注意が必要です。
なお、普通にリダイレクトを指定すると、標準エラー出力への出力はリダイレクト先のファイルではなく、端末に表示されます。これは、エラーが起きたことをユーザーに知らせるためです。この標準エラー出力もリダイレクトするなら、
$ ./sysbackup.sh > log.txt 2> err.txt
とします。「2>」というのがミソで、これが標準エラー出力のリダイレクトの指示です。さらに、
$ ./sysbackup.sh > log.txt 2>&1
とすると、すべての出力がlog.txtに記録されます。
長い文字列を出力する
シェルスクリプトで文字列を出力するには、普通echoコマンドを使って、
echo 'Hello, world'
などとします。しかし、ちょっと長い文字列を出力したいこともあるでしょう。また、HTMLファイルへの加工を行うなら、ヘッダなどを見やすい形で記述したいと思うでしょう。こんなときに便利なのが、「ヒアドキュメント」です。
例えば、HTMLのスケルトンをechoコマンドで出力するなら、
#!/bin/sh echo "<HTML>" echo "<HEAD>" echo "<TITLE></TITLE>" echo "</HEAD>" echo "<BODY>" echo "<H1></H1>" echo "<P>" echo "</P>" echo "</BODY>" echo "</HTML>"
ですが、ヒアドキュメントを使うと、
#!/bin/sh cat << EOS <HTML> <HEAD> <TITLE></TITLE> </HEAD> <BODY> <H1></H1> <P> </P> </BODY> </HTML> EOS
となります。「<<」の後に指定した文字列が出現する直前まで、コマンドに対する標準入力として扱われます。上の例では、「EOS」を目印にしています。
ヒアドキュメントを使うと、出力したい文字列をそのまま書けばいいので、スクリプトをすっきりと記述できます。後から文字を追加するのも簡単です。
引数と変数で柔軟性を実現
引数と変数
いつも同じことを繰り返すシェルスクリプトだけでもかなりの省力化になりますが、処理対象を実行時に決めたいこともあります。シェルスクリプトはテキストファイルなので、そのたびに書き換えるのも1つの手です。しかし、処理対象の数が増えてくると面倒ですし、あまりスマートな方法ではありません。
これを解決するため、シェルスクリプトに引数を渡すことができます。引数は、シェルスクリプトからは順番に$1、$2、$3、……として参照できます。引数の数は「$#」で分かります。また、「$*」とすることで、すべての引数を一度に参照できます。なお、「$0」はスクリプトが呼び出されたときの名前が入ります。
例として、簡単なあいさつを行うシェルスクリプトで引数を試してみましょう。greeting.shという名前で、
#!/bin/sh echo "Hi, $1. I am $0." echo "Hi, $*. I am $0."
という内容のファイルを作ります。chmodコマンドで、直接実行できるようにしておいてください。以下はこのシェルスクリプトの実行例です。
$ ./greeting.sh tom Hi, tom. I am ./greeting.sh. Hi, tom. I am ./greeting.sh. $ ./greeting.sh tom joe Hi, tom. I am ./greeting.sh. Hi, tom joe. I am ./greeting.sh. $ sh greeting.sh tom joe Hi, tom. I am greeting.sh. Hi, tom joe. I am greeting.sh.
実のところ、シェルスクリプトでは「$」で始まる文字列を変数として扱います。より正確にいうと、文字列が$で始まっている場合はその文字列に格納されたデータを取り出して置き換えます。ですから、変数にデータを代入するときは$が不要です。例えば、
test=one echo $test
というシェルスクリプトを実行すると、
$ sh var.sh one
となります。
また、シェルスクリプトでは基本的に変数を文字列として扱います。つまり、
test=1 test=$test+1 echo $test
の実行結果は、
$ sh add.sh 1+1
となります。
変数を数値として扱いたいときは、declareコマンドで指定します。
declare -i test test=1 test=$test+1 echo $test
というシェルスクリプトを実行すると、
$ bash add2.sh 2
と、今度は整数演算を行った結果が返ってきます。「-i」で、整数値として処理することを指定するのです。
なお、変数に何も代入されていない場合は、空の文字列が返ってきます。
変数に対するパターンマッチ
前回、ファイルの拡張子を書き換える例を示したと思います。そこに使ったパターンマッチは4種類あり、非常に汎用性の高いものです。
- ${変数#パターン}
変数の内容について、最初の部分とパターンがマッチしたら、最も短く一致する部分を取り除いた残りの部分を返す。
- ${変数##パターン}
変数の内容について、最初の部分とパターンがマッチしたら、最も長く一致する部分を取り除いた残りの部分を返す。
- ${変数%パターン}
変数の内容について、最後の部分とパターンがマッチしたら、最も短く一致する部分を取り除いた残りの部分を返す。
- ${変数%%パターン}
変数の内容について、最後の部分とパターンがマッチしたら、最も長く一致する部分を取り除いた残りの部分を返す。
変数testpathに/home/sekino/Linux/how.to.linuxという値を設定しておくと、
$ echo ${testpath##/*/} how.to.linux $ echo ${testpath#/*/} sekino/Linux/how.to.linux $ echo ${testpath%%.*} /home/sekino/Linux/how $ echo ${testpath%.*} /home/sekino/Linux/how.to
といった結果になります。また、ファイル拡張子を置き換えるという観点からは、
#!/bin/sh file=scan01.jpg echo ${file} echo ${file%jpg} echo ${file%jpg}png
というスクリプトを実行すると分かりやすいと思います。
Copyright © ITmedia, Inc. All Rights Reserved.