シェル・プログラミング:全ページ版

* 以下の各項が1ページ(約3k程)となっているスライド版もあります。内容やリンクに変化はありません。

目次

1. メタ文字とその処理

 UNIX入門「4. 正規表現」において,コマンド echo * が,現在作業中のディレクトリのファイル一覧を表示することをみました。

% cd ~/My_Programs
% echo *
hello.c hello.out hello.pl hello.sh sample.sh
%

これに対し,次はどうなるでしょうか?

% echo Hello!

結果は...

% echo Hello!
Hello!
%

何故なのでしょうか?UNIX入門「4. 正規表現」では,正規表現を表わす *, [ ], ? があり,今回の場合はありません。実は,シェルは,そのような特殊文字を最初に解釈するという特性を持ちます。そのような特殊文字を見つけると,それらの意味を解釈し,処理するのです。例えば, * を見つけると,カレント・ディレクトリを読み込み,文字検索を開始する訳です。実際,次を実行してみましょう。

% echo *Hello!
echo: No match
%

あくまでも,文字列検索をしていますね。標準入力の "*Hello!" すら,返しません。

 このような特性,すなわち,先ず処理上特別な意味をもつ文字を解釈するという特性は,入出力におけるパイプ "|" やリダイレクト "<" や ">" にも適用されます。このような処理上特別な意味をもつ文字を「メタ文字」といいます。この特性を理解した上で,UNIX の操作,およびプログラミングをすることになります。

■メタ文字 (Meta Characters)
 処理上,特別の意味をもつ文字。シェルでのメタ文字の例。

     /  \  "  `  '  *  |  !  ?  ~  $  <  >  &  [  ]  #

     注) [ と ] はペアのとき,シェルはそれらを特別な意味に解釈する。
         ] と [ ならば,文字検索とは解釈しない。
[目次]

2. 変数:定義と代入

 メタ文字は,変数名に使用可能ではないことは既に理解できたことでしょう。そこで,変数名に使用可能な文字,設定ルール,変数への値の代入を見てみましょう。

[目次]

3. メタ文字からのエスケープ

 シェルはメタ文字を先ず解釈し処理するわけですが (第1項参照),その解釈からエスケープする方法があります。

■メタ文字よりのエスケープ
 'meta', "\meta"

##########
#  例 6
##########
#!/bin/sh

var=Test
echo $var
echo '$var'
echo '\$var'
echo "$var"
echo "\$var"
実行結果)
% sh script
Test    (変数 var に値が代入され,それを表示)
$var    (メタ文字 $ をエスケープ)
\$var   (メタ文字 \ も $ もエスケープ)
Test    (1行目と同じ処理)
$var    ($ をエスケープ)
%

ダブル・クウォーテイション内では,$,\,`command` の3つをメタ文字と解釈します。すなわち,ダブル・クウォーテイション内ではそれら3つからのエスケープはできません。しかしながら,例6より,$\ を解釈し,バックスラッシュ (\) がダブル・クウォーテイション内でのエスケープに使えることが分かります。

##########
#  例 7
##########
#!/bin/sh

var='ls | wc -w'   # ` ではなく '.
echo "'$var' gives us the number of files: `ls | wc -w`"
実行例)
% sh script
'ls | wc -w' gives us the number of files: 6

(" 内では ' を解釈しない。`command` は解釈。)
[目次]

4. スクリプト内のコメント

 スクリプト内にコメント (注釈) を付けながらプログラミングすると,処理の流れが明確になります。コメントは,# を入力した行において,# 以降すべてが対象となります。

注釈: #

#
# 例 8
#
#!/bin/sh

echo '# Example 8'
echo This is an example.
echo This is an ...# example.
実行結果)
% sh script
# Example 8           (# を ' でエスケープ)
This is an example.
This is an ...        (# 以降無視される)
%
[目次]

5. 演算

[目次]

6. if 文

 シェルは,単に,UNIX のコマンド処理ということだけではなく,プログラミング言語としての一面があると感じるのが,「if文」の存在です。

■if 文 ([ ] 部分はオプション)

     if 条件
     then
           処理1
     [elif 条件]
     [then]
          [処理2]
     [else]
          [処理3]
     fi

 次の例は,例2のスクリプトにおいて,スクリプトを実行する段階で変数が代入されなかったとき,相手に尋ねるものです。

#
# 例 11
#
#!/bin/sh

echo '# Example 11'

message=$*

if [ ! -s $message ]
then
     echo -n 'Enter your message: '
     read message
fi

echo "Your message to your boss was: $message"
実行例1)
% sh script "I'm drinking!"
# Example 11
Your message to your boss was: I'm drinking!
%

実行例2)
% sh script
# Example 11
Enter your message: Getting fat?
Your message to your boss was: Getting fat?
%

この例において,[ ] 内の -s は条件判定用の演算子。[ ] は条件評価の命令。

[目次]

7. case 文

 代入された値とパターンマッチを行い,マッチした場合の処理をさせるのが「case 文」です。

■case 文

     case 変数 in [値1] [値2] ...
     パターン1)
          処理1
     ;;
     パターン2)
          処理2
     ;;
     esac

[値1] [値2] ... 部分は,変数に値が代入されていない場合に指定。また,パターンは幾つでも良い。パターン部分には,正規表現が使用可能。また,| で OR を意味する。

#
# 例 12
#
#!/bin/sh

echo '# Example 12'

echo -n 'Do you want to run hello.pl? [y/n]: '
read answer

case $answer in
y*|Y*)        <-- パターンマッチに正規表現が使用可能。| は OR の意。パイプではない。
   echo 'Running hello.pl...'
   perl ~/My_Programs/hello.pl
   exit  <-- スクリプトの処理を終了させる。
;;
n*|N*)
   echo 'Sorry.'
   exit
;;
esac

echo "Uuuhm...You didn't enter y[es] or n[o]."
実行例)
% sh script
# Example 12
Do you want to run hello.pl? [y/n]: yes
Running hello.pl...
Hello World!
Message
%
[目次]

8. for 文

 シェルの for文は,繰り返し処理に使う値を明示します。

■for 文

     for 値 in 値1 値2 ...
     do
          処理
     done

値1 値2 ...」の部分には,$* (標準入力) や `command` (コマンドの標準出力) を代入することも可能。また *txt とすれば拡張子 .txt のファイルすべてに対し同じ処理ができます。

#
# 例 13
#
#!/bin/sh

echo '# Example 13'

echo $# inputed
echo '+------------+'

for i in $*
do
   echo $i
done

echo '+------------+'

for i in "$*"
do
   echo $i
done

echo '+------------+'

for i in "$@"
do
   echo $i
done
実行例)
% sh script a b c 'd e f'
# Example 13
+------------+
4 inputed
+------------+
a
b
c
d
e
f
+------------+
a b c d e f
+------------+
a
b
c
d e f
%

[ノート]引数は4つと読んでいるが,$* には6つ入っている。シェルはメタ文字を取り除き,スペースで区切りられた部分を分解し,それを $* に格納するのである。そこで,ダブル・クウォーテイションで $@ を囲めば,4つになる。ちなみに,$@ をダブル・クウォーテイションで囲まないと $* と同じ結果となる。また,a b c 'd e f' の間のスペースは幾つ入れても同じ結果となる。

[目次]

9. while 文

 指定した条件が満たされる限り,同じ処理を繰り返します。

■while 文

     while 条件
     do
          処理
     done
#
# 例 14
#
#!/bin/sh

echo -n 'Enter your favorite integer: '
read num

i=0
while [ $i -le $num ]
do
   echo $i
   i=`expr $i + 1`
done
実行例)
% sh script
Enter your favorite integer: 167
0
1
2
3
.
.
.
166
167
%
[目次]

10. until 文

 while 文は,指定した条件が満たされる限り処理を繰り返しますが,逆に,指定した条件が満たされるまで処理を繰り返すのが until 文です。

■until 文

     until 条件
     do
          処理
     done
#
# 例 15
#
#!/bin/sh

echo -n 'Enter your favorite number: '
read num
echo "Sleeping $num seconds..."

i=0
until [ $i -ge $num ]
do
   echo -n '...'
   i=`expr $i + 1`
   sleep 1
   echo $i
done
実行例)
% sh script
Enter your favorite number: 7
Sleeping 7 seconds...
...1
...2
...3
...4
...5
...6
...7
%
[目次]

11. ループから抜ける:break

 for, while, until 文は,繰り返し作業(ループ)を行います。それは,指定した条件が満たされる,あるいは,満たされない場合に繰り返しますが,時として,その繰り返し作業を止めさせ,次の作業に移りたい場合もあるでしょう。このような「ループから抜ける」ための命令文が,break です。

■break n
ループ制御の内側から n 番目までを省略。 n を省略した場合,最も内側のループを抜ける。
#
# 例 16
#
#!/bin/sh
echo -n 'Enter your favorite number: '
read num
echo "Sleeping $num seconds..."
i=0
until [ $i -ge $num ]
do
   echo -n '...'
   i=`expr $i + 1`
   sleep 1
   echo $i
   if [ $i -ge 10 ]
   then
      echo "Hurry up!"
      break
   fi
done
実行例)
% sh script
Enter your favorite number: 60
Sleeping 60 seconds...
...1
...2
...3
...4
...5
...6
...7
...9
...10
Hurry up!
%
[目次]

12. ループ中の作業の一部を飛ばす:continue

 for, while, until 文では,dodone の間に幾つかの作業を記述します。しかし,時として,ある条件の下では,すべての作業をさせる必要がないときもあります。ループ内の作業の一部のみをさせ,ループ内の次に飛ばすのが continue です。

■continue n
ループ制御の内側から n 番目までの後の処理を省略。 n を省略した場合,最も内側のループの処理を指定したところから飛ばす。

#
# 例 17
#
#!/bin/sh

echo '# Example 17'
echo 'Searching executables...'
for i in *
do
     if [ ! -x $i ]  
     then
          continue
     fi
     echo "===$i==="
done

実行例)
% sh script
# Example 17
Searching executables...
===MAIL===
===example.sh===
===hello.out===
%
[目次]

13. 関数

 同じ処理を何度となくさせる場合,その処理を「一つの関数」として設定し,スクリプト内でそれを呼び出せばその処理がいつでもできる方が,プログラムとしては簡明になります。この考え方を拡張すれば,1つ1つの小さな,しかし結果に意味・目的を持たせるような処理を「1つの関数」として設定し,関数の羅列でプログラムを組めば,プログラムは,飛躍的に見やすくなることでしょう。ここでは,先ず,数学の関数の考え方を復習し,その後,それをシェル・スクリプトでの関数の設定方法と呼び出し方を見ます。

13.1. 数学の関数

 変数 x にある値を代入すると,変数 y がある一定のルールの下にある値をとるとき,変数 y は変数 x の関数といい,その依存関係を y=f(x) などで表記し,変数 x を関数 f の独立変数,変数 y を従属変数と呼ぶのでした。独立変数 x はベクトルでもよいし,変数 y もベクトルかもしれません。関数の値(y の値)が,変数 x の任意の値に対し一定の場合,これも関数であり,「定値関数」と呼ばれるのも習ったことでしょう。通例,x の値が変われば,「一定のルール」に従い,y の値は変化します。

13.2. シェルでの関数

 上の「一定のルール」を「定めた処理」と置き換えてみましょう。然らば,プログラミングにおける関数は,「予め処理を定める」ことで,関数に渡された値(入力)がその処理に従い,他の値(出力)に変換される仕組みということになります。シェルでの関数の定義方法,およびスクリプト内での変数の代入方法は,以下の通りです。

[目次]