shell script
シェルスクリプト

講義

シェル

ターミナルに対して入力するコマンドを処理するプログラムがシェルである. シェルの機能について説明する.

リダイレクト

通常,コマンドの入力はキーボードであり,出力は画面に設定されている.リダイレクトにより それらをファイルに切り替えることができる.入力の切り替えは "<", 出力の切り替えは ">" で行う.たとえば ls コマンドはカレントディレクトリの ファイル,ディレクトリの一覧を表示するコマンドであり,通常は画面に出力を 表示するが,"ls > file1" とコマンドを入力すると ls コマンドの結果が file1 というファイルに書き込まれる.

パイプ

あるコマンドの出力を直接別のコマンドの入力へ繋げることができる. コマンド 1 の出力をそのままコマンド 2 の入力として使いたい場合, コマンド1 | コマンド2と入力する.例えば ps axu コマンドで現在実行されている プロセスの情報が画面に出力される.この中で "firefox" という文字列がある 行だけを表示したい場合を考える. ps axu コマンドの出力を grep "firefox" の入力とすればよいので "ps axu | grep firefox" とすればよい.

シェルスクリプト

何度もシェルに同じコマンドを実行させるときや,複雑な 処理を行うときなどのために,実行させるコマンドをあらかじめファイルに 記述しておき,それを呼び出して実行することができる. シェルに実行させるコマンドを記述したファイルをシェルスクリプトとよぶ.

シェルスクリプトの実行

ターミナルでディスプレイに Hello world! と表示させたい場合, 文字列を出力するコマンド echo を利用して, 以下のようにコマンドを入力すればよい.

nauser@os-160:~$ echo 'Hello world!' 

同じことを を実行するシェルスクリプトを作成する場合, 以下の内容のファイルを作成する.ファイル名は何でもよい.ここでは ファイル名を hello.sh として自分のホームディレクトリに 保存する.

例 11.1 hello.sh

#!/bin/sh

echo 'Hello world!'

一行目の /bin/sh はシェルプログラムが存在する場所を示す.これはシステムにより 異なる場合がある."which sh" コマンドで sh コマンドの絶対パスを知ることができる.

hello.sh ファイルをコマンドとして実行可能にするには実行パーミッションを つける必要がある.hello.sh ファイルがあるホームディレクトリで ls -l を実行したとき,

-rw-r--r--  1 nauser  nauser   16  2010-06-27 01:00 hello.sh

と表示される場合,実行パーミッション(x)が無い状態である.これを 自分には読み書き実行許可,グループと他人には読みと実行のみ許可 という設定にする場合,-rwxr-xr-x とすればよい.これの 2 進数表現は 111 101 101 すなわち 755 であるので以下のコマンドを入力すればよい.

chmod 755 hello.sh

これで hello.sh に実行属性がついたので hello.sh があるディレクトリにて ./hello.sh コマンドを入力すれば実行できる.

逐次実行

シェルスクリプトに複数のコマンドを列挙すると,上の行のコマンドから順に実行される. 例えば date コマンドを実行したあと ls コマンドを実行するシェルスクリプトは以下のとおりである.

例 11.2

#!/bin/sh

date
ls

引数

シェルスクリプトにいろいろな処理をさせたい場合,引数が与えられると便利である. 例えば引数として指定されたのファイルのコピーを,"もとのファイル名.old" という 名前で作成するスクリプト cpold.sh は以下のように作成することができる.

例 11.3 cpold.sh

#!/bin/sh

cp $1 $1.old 

$1 はスクリプトに対する第一引数を示す.例えばカレントディレクトリに file1 というファイルがある場合,"./cpold.sh file1" コマンドを実行すると file1 ファイルのコピーである file1.old が作成される.

$0 には実行されたコマンド自身(この場合は ./cpold.sh)が格納され, $1, $2,... には順に第 1 引数,第 2 引数... が格納される.

逆クォート

シェルスクリプト中で,逆クォートで囲んだ場所はクォート内のコマンドの 実行結果で置換される.たとえば date コマンドは現在の日時を出力 するコマンドであるが,それを組み込んだコマンドを以下のように作ることができる.

例 11.4

#!/bin/sh

echo "date is `date`"

echo は文字列を出力するコマンドであり,この場合は date is と出力したあと date コマンドの出力内容を出力する.

変数

シェルスクリプトでも変数を利用できる.変数名として利用できる 文字は英数字とアンダースコア"_" であるが,最初の文字には数字 を使うことはできない.

変数へ値を代入するには"変数名=値"と書けばよい. イコールの両側にスペースを入れてはいけないことに気をつける. 変数の値を呼び出すには変数名の前に "$" をつける.次の例は 変数 name に値 hiroshi を設定し, echo 文でそれを出力する スクリプトである.

例 11.5

#!/bin/sh

name=hiroshi
echo "name is $name"

例えば $name の後に x を付け足したい場合,$namex と書くと $namex という名前の変数と解釈されてしまう.これを避けるため, {} を使って変数名の範囲を明示的に指定することができる. この例では ${name}x と書けばよい.

date コマンドのオプションとして,"date +%Y%m%d" とコマンド入力することで日付を 年,月,日の8 桁で出力させることができる.このコマンドと逆クォート,変数, コマンドライン引数を使用することで引数で与えられたファイルのバックアップとして 拡張子に日付を付加したファイルを作成するスクリプト bup1.sh を書くことができる.

例 11.6 bup1.sh

#!/bin/sh

TIME=`date +%Y%m%d`
cp $1 $1.$TIME

条件分岐

条件分岐は本来は

if 条件式
then
    条件式が真の時の処理
else
    条件式が偽の時の処理
fi

の形式で行う.else 節は処理を行わない場合は省略できる. 実際には条件式は [...] の構文を用いることが多いのでその 代表的にな使い方をいくつか説明する.

先述のようにシェルスクリプトへの第 1 引数は $1 に格納され,第 2 引数以降は $2, $3 ... に格納される.例 11.6 のバックアップシェルスクリプト bup1.sh を改良し,$1 の内容が "-l" だった場合に "ロングフォーマット", すなわち "年月日-時分" を拡張子として追加する bup2.sh を作成する.この場合, バックアップするファイル名は第 2 引数で与えられるとする. 例えば file1 に対して 2010 年 6 月 28 日 9:20 に bup2.sh file1 を実行すると今まで通りコピーとして file1.20100628 が作成され,bup2.sh -l file1 を実行すると file1.20100628-0920 が作成されるようにしたい.このスクリプトは 条件式の [...] 記法と $2 以降すべてについて番号を 1 小さいものにセットしなおす操作である shift を利用して以下のように記述することができる.

例 11.7 bup2.sh

#!/bin/sh

if [ $1 = "-l" ]
then
    TIME=`date +%Y%m%d-%H%M`
    shift
else
    TIME=`date +%Y%m%d`
fi

cp $1 $1.$TIME

shift により $2, $3,... に設定されていた値が 1 つずつ左に"シフト"され, $1, $2,... に設定される.

ほかに if がよく利用される場面としてファイルが存在するかどうかで処理を変える場合が ある.これには [ -e ...] 記法を使う.たとえば第 1 引数のファイルがあればその内容を表示, 無ければ無いことを示すメッセージを出力するスクリプトは以下のようになる.

例 11.8

#!/bin/sh

if [ -e $1 ]
then
    cat $1
else
    echo "cannot find $1."
fi

繰り返し

シェルでは繰り返し構文として while, until for が使用できる. while, until の構文はそれぞれ以下の通りである.

while 条件式
do
    処理
done
until 条件式
do
    処理
done

while では条件式が真の間,until では条件式が偽の間処理が繰り返される.

シェルスクリプトでは繰り返しとしては for がよく利用される. 例えば file1, file2, file3 について 順に ls -l を行うコマンドは 以下のように書ける.ここでは $i がループ変数の役割をしている. for の行ではループ変数名に $ がつかないことに注意せよ.これは変数への セットでは $ をつけず,読み出し時に $ をつけるのと同じである.

#!/bin/sh

for i in file1 file2 file3
do
    ls -l $i
done

$* を使って引数すべてを for で処理することができる. $* と書くと,すべての引数($1 以降)をスペースで区切って繋げた文字列 として解釈できるので for 分の in 以降にそのまま使うことができる. 以下は bup2.sh を改良し,複数のファイルを引数としてとることのできる バックアップコマンド bup3.sh である.

例 11.9 bup3.sh

#!/bin/sh

if [ $1 = "-l" ]
then
    TIME=`date +%Y%m%d-%H%M`
    shift
else
    TIME=`date +%Y%m%d`
fi

for i in $*
do
    cp $i $i.$TIME
done

上のスクリプトで cp を cp -r に変えるとファイルだけでなくディレクトリのバックアップも できるスクリプトを作ることができる.

実習

実習 1

例 11.1 hello.sh を作成し,実行せよ.

実習 2

/ ディレクトリにあるファイルのリストをカレントディレクトリの file1 に書き出し,その書き出したファイルに対して sort -r file1 を実行した結果をカレントディレクトリの file2 に書き出すシェルスクリプトを作成し,実行せよ. sort は -r オプションを与えると逆順にソートを行う.

実習 3

例 11.4 のシェルスクリプトを作成し,実行せよ.

実習 4

例 11.8 のシェルスクリプトを作成し,引数として /etc/resolv.conf を与えて 実行せよ.

実習 5

IP アドレス,DNS サーバの設定は 第 9 回実習の実習 1 の状態に設定せよ.DNS サーバは教卓のみで稼働している設定である. 01 班から 14 班までについて班番号の順に,班名を Aとする.

実習5.1

1 班から 14 班全てに対して nslookup pcA1.1b202.jt.u-tokai.ac.jp を行うシェルスクリプトを作成し実行せよ.

実習5.2

1 班から 14 班全ての pcA1,から pcA4 に対して 2 重ループを使って nslookup を実行する シェルスクリプトを作成し実行せよ.

課題

第 10 回課題「シェルスクリプト」 を提出せよ.


Updated in June 26, 2010, index.html, Yamamoto Hiroshi