Bシェルスクリプト基礎文法最速マスター

Bシェルスクリプトのすすめ

■ はじめに

Bシェルスクリプトの文法一覧です。他の言語をある程度知っている人はこれを読めばBシェルスクリプトシェルの基礎をマスターしてBシェルスクリプトを書くことができるようになっています。簡易リファレンスとしても利用できます。

UNIX(Linux)を使いこなすには、GUI(Graphical User Interface)でなく、CUI(Character User Interface)が必要です。

逆に云えば、コマンドを文字入力して、いかにUNIX(Linux)に仕事をさせるのかが、UNIX(Linux)を使いこなすと云うことです。

そして、文字入力したコマンドを解釈するものをシェルと云い、UNIX(Linux)には沢山の種類のシェルがあります。

で今回、UNIX(Linux)ならどのOSでも必ずバンドルされているBシェル(sh)のスクリプトを勉強いたしましょう…ということです。

とにかく、シェルスクリプトは沢山ありますが、その起源のBシェルスクリプトをマスターすれば、すべてに応用がききますよ!

そう、Bシェルスクリプトこそすべてのスクリプトの基礎になったスクリプトなのです(たぶん)。

■ シェルとは

UNIX(Linux)を一回でも使った人はおわかりと思いますが、ログインするとコマンドプロンプトがでます。

そして、ls等のコマンドを入力します。すると、コマンドが実行され結果が表示されます。

実は、ls等のコマンド名を解釈して実際のコマンドを呼び出し、実行させるものをシェルと云います。

また、複数のコマンドをテキストファイルに記述して、それをコマンドとして実行させることが出来ます。

このような、コマンド(テキストファイル)をシェルスクリプトと云います。

まぁ、前書きはこのぐらいにして、では本文に参ります。

■目次:

■本文:

◆ Bシェルスクリプトの書き方と実行方法について

以下のサンプルは、ごく簡単なBシェルスクリプトです。

viエディッタ等を使って以下の2行を書きこんだtest01.shファイルを用意してください。

ファイル名 : test01.sh

#!/usr/bin/sh
echo これはBシェルスクリプトです。

1行目は、シバン(shebang)と云い、その後に続く部分をインタプリンタのパス名を指定します。ここでは「/usr/bin/sh」というプログラムを指定しています。しかし、システムによって違う場合もありますので、事前につぎのコマンドで確認することが出来ます。

$ which sh 

Bシェルスクリプトとは、基本的には『テキストファイル内にUNIX(Linux)コマンドを書き並べたもの』です。 2行目はこのスクリプトで実行されるコマンドです。

実行結果:

$ chmod u+x test01.sh
$ test01.sh
これはBシェルスクリプトです。

1行目のchmodコマンドでスクリプトファイルに実行権を与えています。これをしないと実行権限がないために、スクリプトの実行ができません。

2行目で実際にスクリプトを実行しています。システムによってはパス設定の関係で、./test1.sh」としなければならない場合もあります。

3行目が実行結果です。スクリプト2行目の『echo これはBシェルスクリプトです。』の実行結果が出力されました。

基本は、これだけです。しかし、この後に解説する制御文や変数のコマンド置換、引数渡し等を組み合わせると、非常に強力なツールになることを実感できるでしょう。

◆ コメントと改行について

普通、スクリプトのみでは分かり辛く、特に分かり難い所には、コメントを入れます。

Bシェルスクリプトでは、以下のスクリプトように、行の頭に「#」をつけることでシバン以外はコメントとすることができます。

ファイル名 : test012.sh

#!/usr/bin/sh
# ここはコメントです。
echo "これはBスクリプト本体です。"

実行結果:

$ chmod u+x test02.sh 
$ test02.sh
これはBスクリプト本体です。

スクリプト中の2行目は行頭に「#」がついているために、コメントとなり、実行結果に影響いたしません。

また、Bシェルスクリプトでは一行の文字数が長くなると、まぁ、見にくくなるものです。

そこで、適当に改行をするのがよいでしょう。

Bシェルスクリプトにおいて、行の途中で改行するには、以下のように改行する直前に「\」を入れて改行をエスケープします。

ファイル名 : test03.sh

#!/usr/bin/sh
echo "これはBシェルスクリプトです。\
改行をエスケープすると2行に記述できます。"

実行結果:

$ chmod u+x test03.sh
$ test03.sh
これはBシェルスクリプトです。改行をエスケープすると2行に記述できます。

◆ 変数について

変数名に使える文字は、英数字と「 _ 」(アンダーバー)のみで、一文字目に数字は使えません。

変数に値を代入するには「=」で変数名と値を結びます。「=」の左右に半角スペースを入れてはいけません。

また、変数の値を参照するには「$変数名」とします。

ファイル名 : test04.sh

#!/usr/bin/sh
var="変数のテストです。"
echo $var

実行結果:

$ chmod u+x test04.sh
$ test04.sh
変数のテストです。

注意点:

◆ 未設定変数の扱いについて

Bシェルスクリプトでは、変数の中身が空の時や、変数そのものが未定義の時に、いろいろな処理を行うことが可能です。

以下にその一覧を示します。

書式 変数が空でないとき 変数が空のとき
${変数 :-word } 変数 の値が使われる word の値が使われる
${変数 :=word } 変数 の値が使われる word の値が変数に代入されて使われる
${変数 :?word } 変数 の値が使われる word がエラーメッセージとして使われ、スクリプトが終了する
${変数 :+word } word が変数 に代入される 何もしない

書式 変数が定義されているとき 変数が定義されていないとき
${変数 -word } 変数 の値が使われる word の値が使われる
${変数 =word } 変数 の値が使われる word の値が変数に代入されて使われる
${変数 ?word } 変数 の値が使われる word がエラーメッセージとして使われ、スクリプトが終了する
${変数 +word } word が変数 に代入される 何もしない

・サンプル

ファイル名 : test17.sh

#!/usr/bin/sh
echo Are you name ? $who
echo Are you name ? ${who:-Chaichan}
echo $who
who=Macha
echo Are you name ? ${who:-Chaichan}

・実行結果は下のようになります。

$ chmod u+x test17.sh
$ test17.sh
Are you name ?
Are you name ? Chaichan

Are you name ? Macha

ファイル名 : test18.sh

#!/usr/bin/sh
echo Are you name ? $who
echo Are you name ? ${who:=Chaichan}
echo $who
who=Macha
echo Are you name ? ${who:=Chaichan}

・実行結果は下のようになります。

$ chmod u+x test18.sh
$ test18.sh
Are you name ?
Are you name ? Chaichan
Chaichan
Are you name ? Macha

ファイル名 : test19.sh

#!/usr/bin/sh
who=Macha
echo Are you name ? $who
echo Are you name ? ${who:?"END Messege1"}
who=""
echo $who
echo Are you name ? ${who:?"END Messege2"}

・実行結果は下のようになります。

$ chmod u+x test19.sh
$ test19.sh
Are you name ? Macha
Are you name ? Macha

test19.sh: who: END Messege2

ファイル名 : test20.sh

#!/usr/bin/sh
echo Are you name ? $who
echo Are you name ? ${who:+Chaichan}
echo $who
who=Macha
echo Are you name ? ${who:+Chaichan}

・実行結果は下のようになります。

$ chmod u+x test20.sh
$ test20.sh
Are you name ?
Are you name ?

Are you name ? Chaichan

ファイル名 : test21.sh

#!/usr/bin/sh
who="Macha"
echo Are you name ? $who
echo Are you name ? ${who-Chaichan}
echo $who
echo Are you name ? ${who2-Chaichan}

・実行結果は下のようになります。

$ chmod u+x test21.sh
$ test21.sh
Are you name ? Macha
Are you name ? Macha
Macha
Are you name ? Chaichan

ファイル名 : test22.sh

#!/usr/bin/sh
who="Macha"
echo Are you name ? $who
echo Are you name ? ${who=Chaichan}
echo $who
echo Are you name ? ${who2=Chaichan}

・実行結果は下のようになります。

$ chmod u+x test22.sh
$ test22.sh
Are you name ? Macha
Are you name ? Macha
Macha
Are you name ? Chaichan

ファイル名 : test23.sh

#!/usr/bin/sh
who=Macha
echo Are you name ? $who
echo Are you name ? ${who?"END Messege1"}
echo $who1
echo Are you name ? ${who1?"END Messege2"}

・実行結果は下のようになります。

$ chmod u+x test23.sh
$ test23.sh
Are you name ? Macha
Are you name ? Macha

test23.sh: who1: END Messege2

ファイル名 : test24.sh

#!/usr/bin/sh
who="Macha"
echo Are you name ? $who
echo Are you name ? ${who+Chaichan}
echo $who
echo Are you name ? ${who2+Chaichan}

・実行結果は下のようになります。

$ chmod u+x test24.sh
$ test24.sh
Are you name ? Macha
Are you name ? Chaichan
Macha
Are you name ?

◆ 特殊な変数について

Bシェルには、あらかじめ定義されている特別な意味を持つ変数があります。

以下は、主なそれの一覧です。

サンプルで確認してみましょう。

ファイル名 : test05.sh

#!/usr/bin/sh
echo "引数の数は $# 個です。"
echo "スクリプトの名前は $0 です。"
echo "1番目の引数は $1 です。"
echo "2番目の引数は $2 です。"
echo "\$*の引数は $* です。"
echo "\$@の引数は $@ です。"
echo "プロセスIDは $$ です。"

実行結果は下のようになります。

$ chmod u+x test05.sh
test05.sh aaa bbb
引数の数は 2 個です。
スクリプトの名前は test05.sh です。
1番目の引数は aaa です。
2番目の引数は bbb です。
$*の引数は aaa bbb です。
$@の引数は aaa bbb です。
プロセスIDは 15999 です。

◆ コマンド置換について

コマンドラインやシェルスクリプト中でコマンドをバッククォーテーション ( ` ) で囲むと、自動的にそのコマンドを実行した結果に置き換わります。

ファイル名 : test06.sh

#!/usr/bin/sh
var=`date`
echo $var
echo "今日は `date` です。"

実行結果は以下のようになります。

$ chmod u+x test06.sh
$ test06.sh
2004年06月03日 (木) 12時59分20秒 JST
今日は 2004年06月03日 (木) 12時59分20秒 JST です。

変数 var に date コマンドを実行した結果が代入されて、 date コマンドの出力結果が出力されます。

また、変数に代入しなくても、スクリプト中でバッククォーテーションで囲まれたものはコマンドとして解釈されます。

◆ エスケープシーケンスと文字列連結について

下に並べた文字は、シェルスクリプトでは特殊な文字として扱われ、そのままでは普通の文字として扱ってくれません。

; & ( ) | ^ < > ? * [ ] $ ` " ' { } [TAB] [SPACE]

これらを普通の文字として扱うためには、以下のように3通りのエスケープ方法があります。

  1. バックスラッシュ( \ )により一文字エスケープ

    バックスラッシュ ( \ ) の直後の特殊文字は普通の文字として扱われます。

  2. シングルクォーテーション( ' )で囲む

    シングルクォーテーション ( ' ) で囲まれた中の特殊文字はすべて普通の文字として扱われます。

  3. ダブルクォーテーション( " )で囲む

    ダブルクォーテーションで囲まれた中の特殊文字は $ ` \ を除いてすべて普通の文字として扱われます。つまり、ダブルクォーテーション中では 変数、コマンド置換 が行われます。これが、シングルとダブルのクォーテーションの意味の違いです。

サンプルスクリプト

ファイル名 : test08.sh

#!/usr/bin/sh
var=hensunoatai
echo $var
echo "$var"
echo '$var'
echo '\$var'

実行結果は下のとおり。

$ chmod u+x test08.sh
$ test08.sh
hensunoatai
hensunoatai
$var
\$var

・文字列の連結

シェルスクリプトで文字列を連結するには、単純に文字列同士を並べるだけです。

ファイル名 : test09.sh

#!/usr/bin/sh
echo "おはよう!""さようなら…"

実行結果は以下のようになります。

$ chmod u+x test09.sh
$ test09.sh
おはよう!さようなら…

◆ 四則演算について

Bシェルスクリプトでは、変数は文字列として扱われ、数値として四則演算を行うには、expr というコマンドを使用します。

ファイル名 : test10.sh

#!/usr/bin/sh
echo "8 + 2 = `expr 8 + 2`"
echo "8 - 2 = `expr 8 - 2`"
echo "8 * 2 = `expr 8 \* 2`"
echo "8 / 2 = `expr 8 / 2`"
echo "8 % 2 = `expr 8 % 2`"

実行結果は以下のようになります。

$ chmod u+x test10.sh
$ test7.sh
8 + 2 = 10
8 - 2 = 6
8 * 2 = 16
8 / 2 = 4
8 % 2 = 0

exprコマンドの乗算の演算子は「*」ですが、シェルスクリプトが「*」を特殊文字として解釈するので「\」によりエスケープします。

◆制御文:ifとforについて

条件によって処理を分岐したい際に if文を使います。

if 条件; then
コマンド
.....
elif
コマンド
.....
else
コマンド
.....
fi

・if文に関するサンプル

ファイル名 : test11.sh

#!/usr/bin/sh
if [ $1 = "argment" ]; then
echo "引数はargmentです。"
elif [ $1 = "hikisu" ]; then
echo "引数はhikisuです。"
else
echo "引数は$1です。"
fi

実行結果は以下のようになります。

$ chmod u+x test11.sh
$ test11.sh argment
引数はargmentです。

リストに対しての繰り返しの処理にはfor文を使います。

for文 for 変数 in リスト
do
コマンド
done

・サンプルを示します。

ファイル名 : test12.sh

#!/usr/bin/sh
for var in x y z
do
echo $var
done

実行結果は以下のようになります。

$ chmod u+x test12.sh
$ test12.sh
x
y
z

「リスト」は半角スペースか改行で区切ります。

変数が順に「リスト」の要素に置換され、do と done の間のコマンドが処理されています。

◆ 制御文:whileとcaseについて

繰り返しの処理には、while文を使います。

while文 while 条件
do
コマンド
done

条件が真である間 do done をループします。

・サンプルを示します。

ファイル名 : test13.sh

#!/usr/bin/sh
a=2
while [ $a -lt 4 ]
do
echo "a = $a"
a=`expr $a + 1`
done

実行結果は以下のようになります。

$ chmod u+x test13.sh
$ test13.sh
a = 2
a = 3

複数の条件の時は、case文を使います。

case 変数 in
パターン1) コマンド;;
パターン2) コマンド;;
パターン3) コマンド;;
esac

変数が一致するパターンの後ろのコマンドが実行されます。パターンにはワイルドカードが使えます。

・ワイルドカード

*すべての文字列を表す。
?任意の1文字を表す。
[...]鉤括弧内のどれか1文字を表す。
[!...]鉤括弧内に含まれない文字を表す。

尚、Bシェルでのパターンは、正規表現でなく、あくまでもワイルドカードであることに注意して下さい。

サンプルを示します。

ファイル名 : test14.sh

#!/usr/bin/sh
case $1 in
X) echo "引数はXです。";;
Y) echo "引数はYす。";;
Z) echo "引数はZです。";;
esac

実行結果は以下のようになります。

$ chmod u+x test14.sh
$ test14.sh X
引数はXです。
$ test14.sh Z
引数はZです。

◆ 条件の評価について

if文やwhile文で使用する条件判定は、実は、testコマンドで実現されます。

testコマンドに関しては、『testコマンドの使い方』を参照して下さい。

・サンプル

ファイル名 : test15.sh

#!/usr/bin/sh
if [ -n $1 ]; then
echo "引数があります。"
fi
if [ $1 -gt 10 ]; then
echo "10より大きい数の引数です。"
fi

実行結果は以下のようになります。

$ chmod u+x test15.sh
$ test15.sh 15
引数があります。
10より大きい数の引数です。

◆ 関数の定義と使用について

Bシェルスクリプトは、よく利用する複数のコマンド等を一つまとめに関数化することが出来ます。

・関数は以下のように定義します。

関数名() {
コマンド
.....
}

・サンプル

ファイル名 : test16.sh

#!/usr/bin/sh
func() {
echo "関数から表示。"
echo "関数の引数は $1 です。"
}

func arg1

実行結果は次のようになります。

$ chmod u+x test16.sh
$ test11.sh
関数から表示。
関数の引数は $1 です。

・関数の使用(呼び出す)には

単純にスクリプト中で関数名をコールします。

また、関数に引数を渡すことができ、関数の中では、この引数を $1, $2, ... , $8, $9 で使うことができます。

◆ 入出力(リダイレクション)について

リダイレクションというのは,入力元や出力先を変更する機能です。

このリダイレクション機能を使うとファイルからコマンドにデータを入力したり,処理結果をファイルに書き出したりすることができます。以下のように『>』『<』を使って表現します。

p1 | p2 : p1の標準出力をp2の標準入力にする。尚、『|』は、リダイレクションでなくパイプと云う。
>file 標準出力をfileに書き出す。
>>file 標準出力をfileに追加書きする。
>&m 標準出力をファイルディスクリプタm番に向ける。
>&- 標準出力を閉じる。
n>file ファイルディスクリプタn番の出力をfileに書き出す。
n>>file ファイルディスクリプタn番の出力をfileに追加書きする。
n>&m ファイルディスクリプタn番の出力をファイルディスクリプタm番に向ける。
n>&- ファイルディスクリプタn番を閉じる。
<file fileから標準入力を読みこむ。
<&m ファイルディスクリプタm番からら標準入力を読みこむ。

n<&m ファイルディスクリプタm番の入力をファイルディスクリプタn番に向ける。
<&- 標準入力を閉じる。
n<&- ファイルディスクリプタn番を閉じる。

・サンプル

$ ls > /dev/null 2>&1

lsコマンドの標準出力をゴミ箱(/dev/null)に向け、つぎに標準エラーを標準出力向けています。

すると、結果的に標準エラーもゴミ箱へ向けたことになり、標準出力・標準エラーともにゴミ箱に行きます。

尚、標準出力のファイルディスクリプは『1』で、標準エラーのファイルディスクリプは『2』です。

ちなみに、標準入力は『0』です。

また、たとえば、上記を以下のようにした場合は、思わぬ結果になります。

$ ls 2>&1 > /dev/null

左から順に解釈されるので、まず、標準エラーが画面(標準出力)表示され、標準出力はゴミ箱へ行きます。

複雑なリダイレクトは以下を参照のこと。

http://www.netfort.gr.jp/~tomokuni/lms/shell/text/shell3.txt

・ヒヤ・ドキュメント

コマンドに複数行の文字列を標準入力させることができます。これをヒヤ・ドキュメントと云います。

command <<[-]word : ヒヤ・ドキュメント(メタキャラクタを展開する)
command <<[-]'word' : ヒヤ・ドキュメント(メタキャラクタを展開しない)

尚、[-]の-を使用すると先頭からのタブが無視されます。

・サンプル

ファイル名 : test25.sh

#!/usr/bin/sh
wknm=chaichan
cat << END
macha
$wknm
yasu
END
echo "-------------------"
cat << 'END'
macha
$wknm
yasu
END

・実行結果は次のようになります。

$ chmod u+x test25.sh
$ test25.sh
macha
chaichan
yasu
-------------------
macha
$wknm
yasu

◆ 組み込みコマンドについて

・testコマンドの使い方

testコマンドは以下のように使用します。

test 条件文

条件文は、文字列比較、数値比較、ファイルテストの3つのパターンがあります。

また、条件文は、testコマンドに引数となります。

条件文を引数として受け取ったtestコマンドは

条件が成り立つ(真)なら0、
条件が成り立たない(偽)なら0以外

という終了値を返します。

if文やwhile文の条件リストとして

if test "$HOME" = "/home/chaichan" ; then

のようにtestコマンドをそのまま使うと、少し見辛くなり、

そこで、"["と言うコマンドがtestコマンドのエイリアスとして用意されています。

これを使うと

if [ "$HOME" = "/home/chaichan" ] ; then

のように、見やすく記述することが出来きます。

ちなみに、『]』 というのは 『[コマンド』の引数で、

見た目を良くするためだけの意味のない引数になります。

・文字列比較

文字列の比較を行うには、以下の書式になります。

-n s1 文字列s1の長さが0より大きいとき真。
s1 文字列s1がNULLでないとき真。
-z s1 文字列s1の長さが0のとき真。
s1 = s2 2つの文字列が等しいとき真。
s1 != s2 2つの文字列が等しくないとき真。

例えば、変数 NAME に入っている文字列が "chaichan"と等しいかどうかを

評価するには、以下のようにする。

test "$NAME" = "chaichan"

尚、第1引数は $NAME をダブルクォーテーションで囲みましょう。

これは、NAME が空っぽだったり未定義だった場合、

ダブルクォーテーションで囲まないと上記のコマンドは

test = "chaichan"

のように変数展開で第1引数が消えてしまい、エラーとなってしまいます。

従って、ダブルクォーテーションで囲む必要があるのです。

・数値比較

数値の比較を行うには、以下の書式になります。

n1 -eq n2 n1とn2が等しいとき真。
n1 -ne n2 n1とn2が等しくないとき真。
n1 -gt n2 n1 > n2 なら真。
n1 -ge n2 n1 ≧ n2 なら真。
n1 -lt n2 n1 < n2 なら真。
n1 -le n2 n1 ≦ n2 なら真。
! n1 論理演算NOT。
n1 -a n2 論理演算AND。
n1 -o n2 論理演算OR。

論理演算は、文字列比較、数値比較、ファイルテストの結果に対して

演算を実行できます。これを使えば、二つの変数 WK1 と WK2

を同時に評価する事ができます。

test "$WK1" = "chaichan" -a "$WK2" = "macha"

・ファイルテスト

ファイルの特性、パーミッション、ファイルサイズについて評価できます。以下の書式になります。

-b ファイル ファイルがブロック特殊ファイルのとき真。
-c ファイル ファイルがキャラクタ特殊ファイルのとき真。
-d ファイル ファイルがディレクトリのとき真。
-f ファイル ファイルがノーマルファイルのとき真。
-r ファイル ファイルが読み出し可能のとき真。
-s ファイル ファイルの長さが1バイト以上あるとき真。
-w ファイル ファイルが書き込み可能のとき真。
-x ファイル ファイルが実行可能のとき真。

たえば、${HOME}/chaichan というディレクトリが無かったら作成するという操作は、 以下のように行う。

if [ -d ${HOME}/chaichan ] ; then
mkdir ${HOME}/chaichan
fi  

(現状testコマンドのみですが、eval,execコマンド等も解説する予定です)

◆ BシェルスクリプトとCシェルスクリプトの相違点について

私が解説するより、数段素晴らしいページがありますので、以下を参照して下さい。

複雑なリダイレクトを使用しなければ、Cシェルスクリプトでもそこそこ使えると思うのですが……。

◆ Bシェルスクリプトのデバックと注意点について

Bシェルスクリプトのデバックですが、机上でよくチェックし、それでもわからない時は、echoコマンドでスナップショットをすれば、大体なんとかなります。

しかし、Bシェルには、予め xオプションでの実行時チェック機能があります。

また、、 nオプションでの構文チェック機能(スクリプトは実行されない)もあります。

たとえば、test13.shを実行時チェックをしてみると。

$ sh -x test13.sh
a=2
+ [ 2 -lt 4 ]
+ echo a = 2
a = 2
+ expr 2 + 1
a=3
+ [ 3 -lt 4 ]
+ echo a = 3
a = 3
+ expr 3 + 1
a=4
+ [ 4 -lt 4 ]

xオプションを使用すると、上記のように実行状態が表示される。

また、test13.sh内のdoneをxdoneにして構文チェックをしてみると。

$ sh -n test13.sh
test13.sh: syntax error at line 8: `end of file' unexpected

nオプションを使用すると、上記のように構文チェックされ、しかし、スクリプトそのものは実行されない。

◆ 参考URL

本コンテンツを作成するにあたり、大変お世話になったサイトです。

ここに謹んで御礼申し上げます。

Home

人のよいところをどんどん見つけよう