意外に知らない人が多いシェルスクリプトについて基本的な部分の解説を書いてみました。 主に初心者をターゲットとした内容になっています。中〜上級者はこちらを参照してください。

内容は随時更新していきます。

ここに書かれた情報は完全に正確ではないかもしれませんが大よそ間違いはないと思います。 また、文中にある筆者のコーディングスタイルは、それなりに洗練されていると思うので推奨します。

シェルスクリプトとは?

複数のコマンドと条件分岐・ループによって一連の処理を実行するプログラムのことです。 bash、zshなどのシェルを利用したスクリプトなのでシェルスクリプトと呼ばれます。

※「シェルスクリプト」を単に「シェル」と呼ぶ人がいますがそれは間違い。 「シェル」と「シェルスクリプト」は無関係ではないが別物です。

シェルスクリプトを作成する

まず簡単なシェルスクリプトを作成してみます。 vi で helloworld.sh という名前で新規ファイルを作成し、以下の内容を入力してみてください。

#!/bin/bash

echo "Hello World !"

exit 0

先頭に指定した #! で始まる文字列はシェルスクリプトを実行するためのインタプリタを指定しています。 つまり、「このシェルスクリプトは bash によって実行されます」ということを意味しています。 これは決まり文句のようなものなので必ず1行目に指定してください。

※「インタプリタ」とはシェルスクリプトの内容をその場で解析して実行してくれる機能を指します。

シェルスクリプトを実行する

それではさっそく helloworld.sh を実行してみます。 プログラムを実行するには実行権が必要です。 シェルスクリプトに実行権があるか ls -l で確認してください。

$ ls -l helloworld.sh
-rw-r--r--    1 sunone  sunone        43 Mar 10 18:49 helloworld.sh

ls -l の結果の一番左から2〜4文字目が自分の持っている権限を表しています。 rw- なので読み込み権限と書き込み権限があることになります。

実行権にあたる x がないので、chmod コマンドでこれを付与してやります。

$ chmod +x helloworld.sh

ではもう一度確認してみましょう。

$ ls -l helloworld.sh
-rwxr-xr-x    1 sunone  sunone        43 Mar 10 18:49 helloworld.sh*

今度は rwx になって実行権が付与されているのが分かります。 行の一番最後にある * は対象のファイルに実行権があることを意味しています。 (※設定によってはこの * は表示されません。)

実行権が付与されたので実行してみます。

$ helloworld.sh
-bash: helloworld.sh: command not found

「コマンドが見つからない」と言われてエラーになってしまいました。 これは読んで字のごとく、シェルがコマンドを見つけられないために発生したエラーです。 シェルは実行したコマンドがカレントディレクトリ内にあったとしてもそれを実行してはくれません。

シェルがコマンドを実行してくれるのは、コマンドをフルパス(相対パス)指定で実行したとき、 もしくは環境変数 PATH に設定されているディレクトリ内にコマンドが存在する場合のみです。

ls や chmod がコマンド名だけで実行できるのは、環境変数 PATH にこれらコマンドが存在する /bin が設定されているためです。

$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin

したがって、helloworld.sh を実行するには環境変数 PATH にカレントディレクトリ(.)を 設定してもよいのですが、一般にカレントディレクトリを環境変数 PATH に設定するのは セキュリティ上好ましくないとされています。 (※個人で使用している非公開サーバ等なら、そこまで考える必要もないとは思いますが・・・。)

参考までに設定方法は以下の通りです。export コマンドで現在ログイン中のシェルとそこから派生したシェルで変数の値を有効にすることができます。

$ export PATH="${PATH}:."

なお、シェル変数に値を設定する場合は、= の前後にスペースが入ってはいけません。

str="hoge"

今回はセキュリティを考慮してカレントディレクトリの環境変数への追加は行わず、相対パス指定で実行してみます。

$ ./helloworld.sh
Hello World !

今度は正しく実行できました。

シェルスクリプトの文法

シェルスクリプトにも他のプログラミング言語と同じようにif、for、while、case が使用できます。

まず始めに if 文の使用例から。

# シャープから行末はコメントになります。

# if文の使用例
if [ 0 -eq 0 ]; then
  echo "equal."
fi

条件式には [ コマンドを使用します。これは文法の一部ではなく /bin/[ というコマンドです。 したがって [ の前後は必ずスペースが必要になります。 それに続いている 0 や -eq もパラメータであるため、区切りとしてスペースが必要になります。

; は同一行に複数コマンドを記述する場合に区切りとして使用します。 then は本来、改行後に記述する必要がありますが、行の無駄なので ; を使用して if と同一行に記述します。

; 前後にスペースは不要ですが、筆者は後ろにのみスペースを入れるスタイルを推奨します。

if 文での複数分岐も可能です。

# read コマンドでキーボードから入力した文字列を変数 str に設定する read str # 変数は必ず "" で囲んで使用する(値が入っていない場合、"" がないと文法エラーになるため)
if [ "$str" = "hoge" ]; then
  echo "hoge"
elif [ "$str" = "fuga" ]; then
  echo "fuga"
else
  echo "unknown"
fi

elif を増やして更に分岐を加えることも可能ですが、そういった場合は次の case 文の方が便利です。

# 各分岐の最後の ;; を忘れずに
case "$str" in
  "hoge" ) echo "hoge"
           echo "hoge" ;;
  "fuga" ) echo "" ;;
  * ) echo "unknown" ;;
esac

次はループ処理を行う for 文です。動作的には foreach 文と同様です。

# in に続く文字が次々に変数 i に代入される
for i in 0 1 2 3
do
  echo $i
done

while 文のループ継続条件式には、if 文と同様に [ コマンドを使用可能です。

while [ "$str" = "" ]
do
  read str
done

無限ループにするには : コマンド(ヌルコマンド)を指定します。 : コマンドは一切の処理を行わず正常終了するコマンドです。

while :
do
  read str

  if [ "$str" = "end" ]; then
    # break コマンドでループを抜ける
    break
  fi
done

このように while 文や if 文の条件式部分には、[ コマンド以外の任意のコマンドが指定可能です。 それは while 文や if 文が見ているのは条件式ではなく、終了ステータスというコマンドの実行結果を表す数値だからです

# hogefile の中に文字列 hoge があるか?
if grep 'hoge' hogefile >/dev/null 2>&1
  echo "hoge found."
fi

>/dev/null 2>&1 を指定するとコマンドの実行結果やエラー表示をディスプレイに表示しないようにします。 (ちなみに grep コマンドの -sq オプションでも同様の効果を得られます。)

終了ステータスはコマンド終了後に変数 $? へ自動的に設定されています。 通常、コマンド実行成功の場合は「0」、コマンド実行失敗は「0以外」となります。

touch コマンドで 0 バイトのファイル hoge を作成し、それを ls コマンドで参照してみます。

$ touch hoge
$ ls hoge
hoge
$ echo $?
0

直前に hoge ファイルを作成しているので、当然 ls コマンドは成功し、終了ステータスは 0 になります。

続けて rm コマンドで hoge ファイルを削除してから、同様にして ls コマンドを実行してみます。

$ rm hoge
$ ls hoge
ls: hoge: No such file or directory
$ echo $?
2

今度は hoge ファイルが存在しないため、ls コマンドの終了ステータスは 0 以外になっています。