さて, vi の使い方を大雑把に理解したところで 今日のメインである シェルスクリプト について学習しましょう.
これまで見てきたように, シェルはユーザとコンピュータの橋渡しをして, ユーザがコマンドを端末から打ち込む毎に, それを解釈し実行します. 一方, [1.2]節で触れたように, 「プログラムが可能である」という, もう一つの面もあります. この, プログラムとして手続きを書き込んだファイルを 「スクリプト・ファイル」と言います. スクリプト(script)とは劇の「台本」のことであり, 台本を事前に決めていてそれに沿って行わせるためにこの名前があります.
シェル以外にも多くのスクリプト言語, 例えば Perl, Ruby などがあります. もちろん全部を覚える必要は全くありませんが, 一つでも使いこなせると非常に便利です. 機械的な作業を膨大に繰り返す場合, あるいは普段頻繁に行う一連の作業は 機械に作業の手順を教えて人間は楽をするのが賢い方法です. すぐにこういった膨大な作業をする必要にせまられることは無いかも知れませんが, 早いうちに慣れておくと, いざ必要になったとき楽なものです. ここでは Linux で標準シェルである Bash を使ったシェルプログラミングを学習することにします.
具体的にどのようなものか, 簡単な例を見てみましょう.
次のような一連の仕事を行うコマンドを作ることを考えます.
'...'
」や
ダブルクォーテーション「"..."
」ではなく,
バッククォーテーション「`...`
」であることには意味があります.
詳しく知りたい場合は
[3.2.6] 引用符を調べてみましょう).
vi を使って次のようなファイルを作成しよう.
$ vi sample.sh : |
date echo "I am $USER." loginshell=`grep ^$USER: /etc/passwd | cut -d: -f 7` echo "My login shell is $loginshell" echo "Hey $USER !!" |
$ bash sample.shとすると, 次のように出力される.
Fri Nov 9 06:19:57 JST 2002 I am mym. My login shell is /bin/bash Hey mym !! |
この場合, bash をわざわざ起動して, その引数としてファイル名を指定し, そのファイルを bash が解釈して実行しています.
いちいちこのようなことをするのは非能率でもあるので, 以下の手順で先程のファイル(sample.sh)を実行可能なファイルにします.
#!/bin/bash date echo "I am $USER." loginshell=`grep ^$USER: /etc/passwd | cut -d: -f 7` echo "My login shell is $loginshell" echo "Hey $USER !!" |
$ chmod 744 sample.shもしくは
$ chmod u+x sample.sh
$ ls -lF sample.sh -rwxr--r-- 1 inex inex 204 Oct 24 05:12 sample.sh*所有者のパーミッションのところに, 実行権限である「x」がついている ことを確認する.
$ ./sample.sh
この場合, 自動的に(自分が使っている login シェル)が, スクリプトファイルを bash に解釈させ実行させています.
引数とは, 実行時にそのシェルスクリプトに教えて上げたい 追加情報であるとも言えます.
たとえば, コマンド ls は, そのままではカレント・ディレクトリ内の ファイルをリストアップしますが, 引数として特定のディレクトリを指定すると, 指定されたディレクトリ内のファイルをリストアップします. これはコマンド ls に, 引数として指定したディレクトリについて知りたいんだよ, と追加情報を与えているわけです.
コマンドやシェルスクリプトに引数を指定することを, "引数を渡す"とも表現します. シェルスクリプトに渡された引数を 処理したい場合は, $1, $2, ..., $9 というシェル変数を利用します.
例えば, 引数に渡された単語をそのまま表示する シェルスクリプトを考えてみましょう. 次のようなシェルスクリプト(test.sh)を 作成してみてください.
$ vi test.sh
#!/bin/sh
echo $1
$ ./test.sh Hello! Hello! |
--> 引数に渡した Hello! を返す
|
ここでは, 引数の文字を画面に出力するコマンド echo に, シェル変数 $1 を引数として渡しています. このシェル変数 $1 には, シェルスクリプト test.sh に渡された引数 Hello! が入ります. 結果, 画面には Hello! と表示されるでしょう.
引数を参照するシェル変数は9つまで使うことが出来ます.
それぞれ, $1, $2, ... $9 です.
※引数のデフォルト値などを設定するには
文字列演算子等を利用します.
より複雑な処理をこなす場合でも, 大抵は次の3つの処理の組み合わせで表現できます.
順序構造は, 順番にコマンドを実行するものであるから, 特別の制御文は必要ありません.
選択構造には, if , case を使います. 繰り返し構造には, for , while , until を使います.
if | |
---|---|
書式 | if テストコマンド1 then コマンド1 [elif テストコマンド2 then コマンド2] : [else コマンド3] fi |
解説 |
if 文は if と fi で対になって, いれ子を形成しています. まず, if に続く「テストコマンド1」を実行し, それが「真」(0以外の値)を返したら then に続く「コマンド1」が, 「偽」(0)を返したら elif に続く「テストコマンド2」が実行されます. 「テストコマンド2」が「真」なら「コマンド2」が, 「偽」なら 次の elif が…と続いていき, 全てのテストコマンドが「偽」なら 最後に else に続く「コマンド3」が実行されます. elif と「コマンド2」, else と「コマンド3」の部分は省略可能です. また, 「コマンド1」, 「コマンド2」…には, 複数のコマンドからなる コマンド列を記述することも出来ます. if....fi のいれ子は多重に 重ねることが出来ます. if (elif) に続くテストコマンドの文と then に続くコマンドの文は 普通改行します. どうしても1行にまとめて書きたければ, if テストコマンド; then コマンド のようにセミコロンを挟みます. |
case | |
---|---|
書式 | case 文字 in パターン1) コマンド1 ;; パターン2) コマンド2 ;; ・ ・ esac |
解説 |
パターンには, メタキャラクタ |
while, until | |
---|---|
書式 | while コマンド; do コマンド done |
解説 |
while は, while の左に書かれたコマンドが真(0以外の値)を返す限り, do と done の間に書かれたコマンド(列)を実行し続けます. while の代わりに until と書くと, その左に指定されたコマンドが偽(0)を返す限り, do と done の間に書かれたコマンド(列)を実行し続けます. |
#!/bin/sh
case $#
in
0) echo '引数なし' ;;
1)
echo '引数一つ' ;;
2) echo '引数二つ'
;;
*) echo '引数三つ以上' ;;
esac
※ $# は与えられた引数の数を表すシェル変数です.
■引数に指定された複数のファイル名に対し, 実在する物のみファイル名を表示する.#!/bin/sh
while test $# -gt
0
do
if test -f $1;
then
echo
$1
fi
shift
done
※ test は, それに続く式を評価して値を返すコマンドです. 与えるオプションに応じて, 数値に関するテスト, ファイルの型に関するテスト, 文字列に関するテストを行うことが出来ます. 例えば, test -f [ファイル名]とすると, ファイルが存在すれば真, 存在しなければ偽を返します.
■数字の1から9を表示する.#!/bin/sh
number=1
while test $number -ne
10
do
echo
$number
number=`expr $number + 1`
done
※expr コマンドはその引数を1つの式として評価し, その結果を表示します. これを使うと UNIX
で簡単な計算を行い, シェル・スクリプトの中に算術演算処理を含めることが出来ます. なお, number
の式の右辺はクオーテーション('
)ではありません. 注意して下さい. (次節参照)
シェルスクリプトを作成する際, 引用符を用いることがあります. 引用符には以下の3種類があり, それぞれ意味が異なります(bash).
記号 | 呼称 | 意味 |
---|---|---|
'...' |
シングルクォーテーション | 内部の文字列をそのままの文字列として返す. |
"..." |
ダブルクォーテーション | 内部に含まれる変数等 $,`...`,\ を解釈した結果の文字列を返す |
`...` |
バッククォーテーション | 内部にあるコマンド(群)を実行し, その標準出力を文字列として返す. |
実際に, 使ってみて違いを確認してみましょう.
$ var=ls $ echo '$var' $var $ echo "$var" ls $ echo `$var` sample.sh test.sh |
<-- シェル変数 var に ls を代入 --> '' 内の文字列をそのまま返す --> "" 内の変数の値を返す --> `` 内の変数をコマンドとして実行した結果を返す |
より詳しい情報は man bash として bash のマニュアルを参照することで得ることも出来ます. 分からないコマンドや単語は, 困ってないでどんどん調べようね.
それでは >> 本日の課題 にチャレンジ!!シェルにはファイル名が全部わからなくてもファイル名のパターンで 検索できる機能があります. このファイル名とパターンの照合に用いられる特殊な記号を "メタキャラクタ"(ワイルドカード)と言います. メタキャラクタを上手に用いると, タイプする文字数を減らすことができ, 効率的な入力が行えます.
メタキャラクタ | 意味 | 用例 | 用例の意味 |
---|---|---|---|
* |
任意の文字列を表す. | ls /dev/h* |
ディレクトリ"/dev/"以下にある1文字目が"h"で始まるファイル全てを 表示せよ. |
? |
任意の1文字を表す. ?? は任意の2文字になる. | ls -d /??? |
ディレクトリ"/"以下にある3文字からなるディレクトリ全てを 表示せよ. |
[ ] |
[ ] 内に含まれる文字にマッチする. 例えば [a-c]* は abc のいすれかで始まる任意の文字列を表す . []内の先頭に"^"を付けると"否定"と意味になる. | ls -d /[a-c]* ls -d /[^a-c]* |
ディレクトリ"/"以下にある"a,b,c"のいずれかで始まる(始まらない)ディレクトリを表示せよ. |
{ } |
{ }内に含まれる文字列にマッチする. 例えば test.{pl,gif,f} は, test.pl test.gif test.f と入力したことになる. | ls /dev/{hda,hdb}* |
ディレクトリ"/dev/"以下にある"hda,hdb"で始まるファイル全てを 表示せよ. |
UNIX のコマンドは必ず標準入力と呼ばれる共通した入力の受け付け口と, 標準出力と呼ばれる共通した出力の生成, 標準エラー出力と呼ばれる共通したエラーメッセージの生成を行う機能が 備えられています. 複数のコマンドの標準入出力を繋ぎ合わせたり, ファイルへ/からの出力を行うために使う記号は リダイレクションとパイプ(パイプライン)と呼ばれます.
記号 | 意味 |
---|---|
| |
パイプと呼ばれる. program1|program2 とすると program1 の標準出力を program2 につなぐ. |
> |
program >file とすると標準出力を file に切り換える. |
>> |
>>file とすると標準出力を file に追加する. |
2> |
program 2>file とすると標準エラー出力を file に切り換える. |
< |
program <file とすると標準入力として file から読み込む. |
<<string |
ヒア・ドキュメント. string と書かれた行が次に現れるまで, この後に続く行を標準入力にする. |
記号 | 意味 |
---|---|
; |
コマンドの区切り. program1;program2 とすると, まず program1 が実行され, 次に program2 が実行される. |
& |
;と似ている. ただし, program1 の終了を待たない. |
(...) |
... のコマンド(群)をサブシェルのなかで実行する. |
{...} |
... のコマンド(群)をカレントシェルのなかで実行する. |
$1, $2, etc |
$0...$9 はシェルファイルに対する引数に置換される. |
$var |
シェル変数 var の値. |
${var} |
シェル変数 var の値. テキスト部分と連結されたときの混乱を防ぐために ${var} を用いる. |
\ |
エスケープ記号. c がシェルのメタ・キャラクタである場合, \c は c の特別な意味を打ち消し, 単なる文字としての c にする. |
# |
単語の先頭に # があると, その行に書かれた残りの内容はコメントになる. |
var=value |
変数 var への値 value の代入 |
p1 && p2 |
p1 を実行し, もしうまくいったら p2 を実行する. |
p1 || p2 |
p1 を実行し, もしうまくいかなかったら p2 を実行する. |
使い方 | 意味 | 目的 | 例 |
---|---|---|---|
${varname:-word} |
varnameが存在しNULLでない場合, その値を返す.それ以外の場合はwordを返す. | 変数が定義されていない場合にデフォルトの値を返す | ${count:-0}は, countが定義されていなければ0と評価される. |
${varname:=word} |
varnameが存在しNULLでない場合, その値を返す. それ以外の場合はvarnameにwordを設定して返す. | 変数が定義されていない場合にデフォルトの値を設定する. | ${count:=0}は, countが定義されていなければそれに0を設定する. |
${varname:?message} |
varnameが存在しNULLではない場合, その値を返す. それ以外の場合はvarnameのあとmessageを出力し, 現在のコマンドあるいはスクリプトを中止する (対話型シェルではない場合). messageを省略すると デフォルトで "parameter null or not set"が 出力される. | 変数が定義されていない場合に発生するエラーをキャッチする. | ${count:?"undefined!"}は, countが定義されて いなければ, "count:undefined"を出力して終了する. |
${varname:+word} |
varnameが存在しNULLでない場合, wordを返す. それ以外の場合はNULLを返す. | 変数の存在を評価する. | ${count:+1}は, countが定義されていなければ 1("真"の意味)を返す. |
${varname:offset} ${varname:offset:length} |
サブ文字列を展開する. offsetの位置からlength文字 の長さのサブ文字列を$varnameの値から取り出す. 文字の位置は0からカウントする. lengthが省略された場合, offsetの位置から $varnameの終りまでのサブ文字列が返される. offsetが0より小さかった場合, 開始位置は $varnameのおわりからカウントされる. varnameが@の場合, lengthはoffsetを先頭とする 位置パラメータの番号になる. | 文字の一部を返す(サブ文字列またはスライスという). | countがfrogfootmanと設定されている場合, ${count:4}はfootmanを返し, ${count:4:4}はfootを返す. |
|
最終更新日: 2006/11/04(森川 靖大) | Copyright © 2006 inex |