シェルスクリプトの基礎の基礎

シェルスクリプトをこうやって書いとけば? みたいな話をしてたら、「なんで書くだけで機能するんだ、さっぱり意味わからん」と言われてしまいました。それも悲しいので、とりあえずシェルスクリプトというのがどういうものか、というあたりをここに書き留めておきます。今度聞かれたら見せて終らせられるように……


シェルスクリプトとは?

シェルスクリプト(shell script)とは何か、ということを知る必要がある人は、すでにUNIXやLinux等でシェル・オペレーションを行った経験があるでしょう。ふだん、cpでファイルをコピーしたり、catで内容を表示させたりしてるあれです。

しかし、補完機能があるとはいえ、たくさんの操作をいちいち手で入力するのはめんどくさいですよね? 同じような操作を何度も繰り返さなくてはならない時はなおさらです。シェルスクリプトというのは、そういったコマンド入力をまとめて書いておき、一気に処理させるものです。DOSを知ってる方ならバッチファイルを思い出してください。あれとほぼ同じものです。

シェルスクリプトは様々な場面で活用されますが、ここでは普段の生活を便利にする新しいコマンドを作ってみましょう。

スクリプトを書く前に

さっそくスクリプトを書きはじめたいところですが、その前にちょっと下準備をしましょう。mkdir ~/bin して、ユーザー専用のコマンド置場を作っておきます。このディレクトリにスクリプトを書くようにします。echo $PATH して/home/username/binがPATHに含まれてるか確認してください。含まれてなければ含めましょう。やり方は使用してるシェルによります。

sh系(bash等)の場合:
$ PATH="~/bin:$PATH"; export PATH

csh系(tcsh等)の場合:
$ setenv PATH ~/bin:$PATH

この設定を常に有効にするには、お使いのシェルの設定ファイルに同じように書き込みます。bashなら~/.bash_profile、cshなら~/.cshrc、tcshなら~/.tcshrcに書き込めばよいでしょう。

簡単なシェルスクリプト

さあ、いよいよ実際にシェルスクリプトを書いてみましょう。まずは初心者が一番めんどくさいと感じるであろう、tarファイルの解凍を簡単にできるようにしましょう。

#!/bin/sh
gzip -cd $1 | tar xfv -

まずはこれだけです。これを~/bin/untarという名前で保存してください。保存できたら、実行権限を与えてあげましょう。

$ chmod +x ~/bin/untar

これでもう完成です。さっそく使ってみましょう。何かテスト用に適当なtar.gzファイルを用意してください。ここではhoge.tar.gzというファイル名と仮定します。

$ untar hoge.tar.gz

解凍できましたか? できなかったらもういちどよく見て間違いが無いか確認してください。解凍できれば完成です。これで自作のコマンドが一つできました。今までtarにいろいろオプションを与えて解凍していたのが、untar一発で済むようになりましたね。便利だとは思いませんか?

シェルスクリプトのお約束事

さて、先程のシェルスクリプトを解読してみましょう。まずは1行目に注目してください。

#!/bin/sh

これ、どこかで似たようなものを見掛けた事がありませんか? perlで書かれたCGIを設置した事がある人なら、#!/usr/bin/perl といったものを見掛けた事があるはずです。UNIXでは実行権限の与えられたテキストファイルは、1行目にこのような形で指定したコマンドによって駆動される仕組みになっています。/bin/shというのはどんなUNIXにも必ずあるシェルです。/bin/shでスクリプトを書いておけば、この先どんなUNIXを使おうとも同じスクリプトが動作するはずです。

次に2行目に着目しましょう。

gzip -cd $1 | tar xfv -

これもよく見掛ける形だと思います。tarファイルの解凍の仕方として紹介される事が多いコマンドですね。違いはtarファイルの名前がある場所に、$1という文字が書かれている事です。この$1という文字は「一つめの引数」を表す「変数」です。同様に、$2は二つめの、$3は三つめのの引数を表す変数です。変数というのはデータを格納するバケツだと思ってください。

ちょっと下のようなスクリプトを書いてみてください。

#!/bin/sh
echo $1
echo $2
echo $3

このスクリプトを~/bin/showargという名前で保存しましょう。chmod +xを忘れずに。実行権限を与えたら、早速実行してみましょう。

$ showarg hoge fuga moge
hoge
fuga
moge

与えた引数が表示されますね。いろんな引数を与えてみてください。そして何かエラーが出る引数に当たったら、どうしてか考えてみてください。対処法はきっとすぐわかると思います。

条件分岐

シェルスクリプトはスクリプトである以上、一つのプログラミング言語です。プログラミング言語は必ず条件分岐というものがあります。この条件分岐を用いて、untarを改造してみましょう。

最初のバージョンのuntarでは、gzipで圧縮されたtar(*.tar.gz)しか解凍できませんでした。しかし最近ではbzip2で圧縮されたもの(*.tar.bz2)や、古代に使われていたcompressで圧縮されたもの(*.tar.Z)も存在します。これらに対応するためにはどうしたらいいでしょうか。

とりあえず今回はbzip2に対応してみましょう。

#!/bin/sh

if [ `echo $1 | sed -e 's/^.*\.//'` = 'bz2' ]; then
       DECODE='bzip2'
else
       DECODE='gzip'
fi

$DECODE -cd $1 | tar xfv -

まずは3行目のif文を見て行きましょう。

if [ `echo $1 | sed -e 's/^.*\.//'` = 'bz2' ]; then

[ ]はtestコマンドの別の書き方です。testコマンドは与えられた条件式に対して真か偽を返します。詳細はman testして見てください。man [ とやっても見られると思います。

[ ]で囲まれた中で、さらにバッククオート(`)に囲まれた部分は、第一引数の頭から一番後ろのピリオド(.)までを削除しています。これで拡張子だけを抽出してるわけですね。この抽出された拡張子がbz2であればこのif文は真となり、4行目の、

       DECODE='bzip2'

これが実行されます。これは変数DECODEにbzip2という文字列を代入しています。代入とは変数というバケツにデータをいれる事を言います。最初に出て来た変数$1はあらかじめ用意されてるバケツで、既に中身が入っていました。今度の変数は自分で用意するバケツです。自分で用意するバケツには好きな名前(ここではDECODE)を付けて構いませんが、既存の変数やコマンドとかぶらないよう、大文字で書くことが多いです。このバケツの中身を変更する事で、さまざまな状況に対応できるようになります。

さて、拡張子がbz2でなければどうなるのでしょうか。このif文は偽となります。5、6、7行目を見てください。

else
       DECODE='gzip'
fi

elseは偽であった場合の動作を指定します。今回はbzip2だけの対応なので、bzip2でなければgzipであると見なします。変数DECODEにgzipという文字列が代入されます。7行目のfiは、そこでif文が終ることを示します。忘れずに付けてください。

さて最後の行です。

$DECODE -cd $1 | tar xfv -

変数DECODEをここで用いてます。/bin/shのシェルスクリプトでは、変数を代入する時は$を付けず、使用する時は$を付けます。ややこしいですが伝統というのはそういうものです。

変数はスクリプト実行時に中に入っている文字列や数字に置き換えられます。つまり、先程のif文が真であればbzip2に、偽であればgzipに変身するのです。

if文が真だった時の最後の行:
bzip2 -cd $1 | tar xfv -

if文が偽だった時の最後の行:
gzip -cd $1 | tar xfv -

もちろん$1も変数ですので、実際にはこれも第一引数として与えられた文字列に置換されます。

さあ、これであなたはgzipとbzip2を自動判別(というほど高度では無いですが)して解凍するシェルスクリプトを書き上げました。ちょっとすごいと思いませんか? それほど難しく無いスクリプトでも、十分実用的なソフトウェアになりますね。もし必要や向上心があればぜひcompress圧縮のtarや、非圧縮のtarへの対応も挑戦してみてください。その場合はcase文を使うと楽かもしれません。man shを読みながら書いてみてください。お使いのUNIXシステムに日本語で書かれたshのmanpageが無い場合は、FreeBSDの日本語マニュアルからshのmanpageを検索すると良いでしょう。日本語で書かれたshのmanpageが見られるはずです。


Sugano "狐志庵" Yoshihisa(E) <koshian@misao.gr.jp>
Back to HOME