このエントリーをはてなブックマークに追加

次に以下の二カ所について解説します。

ひとつはスクリプトの先頭の関数宣言の部分で、もう一つは、スクリプトの最後の終了処理の部分です。

function usage() {
    message=`cat <<EOF

About this script:
    Backup script for files
Usage:
    ${0} targent_file_name [backup_file_name]
    target_file_name : file to backup
    backup_file_name : backup file name
                       default file_name is date and time based file_name
                       yyyymmddhhmmss.tgz like 20140101000000.tgz
EOF
`
    echo "${message}"
    return 0
}

function die() {
    if [ $# -eq 1 ] ; then
        echo "ERROR : ${1}"
    else
        echo "ERROR : unknow error"
    fi
    usage
    exit 1
}

(中略)

if [ $? -eq 0 ] ; then
    exit 0
else
    exit 1
fi

この部分を理解するためにまず、リターンコード、関数を作る方法、ヒアドキュメントを理解する必要があります。 まずこの三つを解説します。

リターンコード

題材のシェルプログラムの最後の部分に以下のような場所があります。

if [ $? -eq 0 ] ; then
    exit 0
else
    exit 1
fi

シェルや多くのLinuxコマンドではリターンコード(戻り値)を返します。 全ての処理が正常に終わった場合は、"exit 0"で終了するのが作法です。 途中の処理が失敗している場合は"exit 1"などで終了しておくことで、スクリプトでエラーが発生していることを利用者に伝えることができます。ここでいう0や1はリターンコードと呼ばれます。特にコマンドが成功したかどうかを気にする必要がない場合は省略してもかまいませんが、そうでない場合は、以下のフォーマットのように、終了ステータスと一緒にスクリプトを終わらせておくとよいと思います。

#!/bin/bash
(処理の記述)
exit 0

リターンコードは、コマンドの実行結果が成功した場合は0、失敗した場合は0以外(通常は1)を返すのが習慣です。

直前のコマンドが成功/失敗を表すリターンコードは$?という特殊変数に格納されています。 以下では$?に格納されているリターンコードを確認しています。 "1回目の"mkdir test"ではディレクトリ作成に成功したので、"echo $?"の結果が0になっていて、2回目の"mkdir test"ではすでにディレクトリがあるのでディレクトリの作成に失敗します。そのため、2回目の"echo $?"の結果は1になっています。

$ mkdir test
$ echo $?
0
$ mkdir test
mkdir: ディレクトリ `test' を作成できません: ファイルが存在します
$ echo $?
1

関数宣言

関数の宣言は以下の書式で行います。

function 関数名() {
     実際の処理
}

引数

宣言"function 関数名()"の部分で引数を指定しておく必要はありません。 引数はシェルの引数と同様に$1~といった特殊変数に格納されています。関数宣言の中でも以下のような特殊変数が利用できます。

変数名 概要
$0 シェルスクリプトのファイル名
$1~$n シェル引数の値。$1は第1引数、$2は第2引数…。
$# シェル引数の数
$* 全引数リスト。"$*"のようにダブルクオートで囲むと、"$1 $2 … $n"形式。
$@ 全引数リスト。"$@"のようにダブルクオートで囲むと、"$1" "$2" … "$n"形式。
$$ 実行中のシェルのプロセスID。
$? 最後に実行したコマンドのリターンコード(戻り値)。通常は成功していれば0
$- シェルの実行オプション(setコマンドや#!/bin/bash行に付けたオプション。
$! シェルが最後に実行したバックグラウンドプロセスのPID。

ただし、$0や$$のように実行中のスクリプトの値も入っていますので注意してください。 $0は関数の名前などではなく、元のスクリプトの名前が入っています。 念のため、以下のスクリプトで試してみます。

#!/bin/bash

echo "script name \$0: "$0
echo "arg 1 \$1: "$1
echo "arg 2 \$2: "$2
echo "arg 3 \$3: "$3
echo "args count \$#: "$#
echo "args list \$@: "$@
echo "args list \$*: "$*
echo "PID \$\$: "$$

function param_test() {
    echo "script name \$0: "$0
    echo "arg 1 \$1: "$1
    echo "arg 2 \$2: "$2
    echo "arg 3 \$3: "$3
    echo "args count \$#: "$#
    echo "args list \$@: "$@
    echo "args list \$*: "$*
    echo "PID \$\$: "$$
}

param_test "func_param_1", "func_param_2"

これを実行した結果は以下のようになります。 $$や$0は元のスクリプトに依存する値になっています。

$ ./param.sh script_param_1, script_param_2, script_param_3
script name $0: ./param.sh
arg 1 $1: script_param_1,
arg 2 $2: script_param_2,
arg 3 $3: script_param_3
args count $#: 3
args list $@: script_param_1, script_param_2, script_param_3
args list $*: script_param_1, script_param_2, script_param_3
PID $$: 13008
script name $0: ./param.sh
arg 1 $1: func_param_1,
arg 2 $2: func_param_2
arg 3 $3:
args count $#: 2
args list $@: func_param_1, func_param_2
args list $*: func_param_1, func_param_2
PID $$: 13008

処理結果

シェルスクリプトにおいては、ほかのプログラミング言語における戻り値のようなものはありません。 代わりに、終了ステータス、標準出力を使用します。 終了ステータスは正常終了であれば0、異常であればそれ以外の値というのが通常です。 return文で0または1を返すことで関数の正常/異常を通知することができます。 また標準出力を使用して、処理結果を返すのが一般的です。

ヒアドキュメント

最後に関数のお話とはあまり関係ないですが、よく使用されるヒアドキュメントについて。

シェルスクリプトでもヒアドキュメントを使用することが可能です。 複数行のテキストを出力するといった場合、以下のように複数行を出力することも可能です。

cat "文字列1行目" >> out.txt
cat "文字列2行目" >> out.txt
cat "文字列3行目" >> out.txt

または

echo "文字列1行目" >> out.txt
echo "文字列2行目" >> out.txt
echo "文字列3行目" >> out.txt

同じことを、ヒアドキュメントで行う場合は以下のようにします。

標準出力に出力する場合は、

cat <<EOF
文字列1行目
文字列2行目
文字列3行目
EOF

ファイルに出力する場合は、

cat <<EOF > out.txt
文字列1行目
文字列2行目
文字列3行目
EOF

変数に格納する場合は、

var=`cat <<EOF
文字列1行目
文字列2行目
文字列3行目
EOF
`

のようにすることができます。

コード解説

ここまでの情報でソースコードを見ていきます。 最初の部分はユーザにヘルプとして操作方法の文章を表示するところです。

function usage() {
    message=`cat <<EOF

About this script:
    Backup script for files
Usage:
    ${0} targent_file_name [backup_file_name]
    target_file_name : file to backup
    backup_file_name : backup file name
                       default file_name is date and time based file_name
                       yyyymmddhhmmss.tgz like 20140101000000.tgz
EOF
`
    echo "${message}"
    return 0
}

ヒアドキュメントを用いて複数行テキストを変数messageに格納してから終了しています。念のため関数が成功したら終了ステータスとして0を返しています。

次の部分は、異常終了時の処理をdie関数にしています。 die関数は、最初のif文で出力すべきメッセージが与えられれば、それをエラーメッセージとして出力し、そうでない場合は不明なエラーである旨の出力をおこないます。 その後、共通的にusage関数を実行して、ユーザにこのシェルスクリプトの使い方の情報を表示してから終了します。 最後に、異常終了のステータスである1とともにexitを用いてプログラム自体を終了します。

function die() {
    if [ $# -eq 1 ] ; then
        echo "ERROR : ${1}"
    else
        echo "ERROR : unknow error"
    fi
    usage
    exit 1
}

シェルプログラム全体では3か所でdie関数が呼ばれますが、このように何度も実行される処理を一つの関数にまとめておくことで、 エラーメッセージの出力形式を共通化したり、仕様変更時の修正箇所を関数内に限ることができます。




記事一覧へ