Linux講座にようこそ。このページは「Linuxの使い方 - 第6章 シェルスクリプトの作り方」です。

Linuxの使い方

6. シェルスクリプトの作り方(8/8)

6.8 関数(2/2)

6.8.2 関数宣言

関数は定義(宣言)しただけでは実行しません。関数呼び出しを行って、初めて実行します。関数が終了すると、呼び出した次のコマンドに制御が移ります。また、関数呼び出しで指定した引数は関数側では位置パラメータとして受け取ります。

形式

関数宣言の形式1と形式2は機能的な相違はありませんので、使いやすいほうを使うと良いでしょう。returnを指定しないと関数の最後のコマンドを実行して終了します。

なお、returnで返せるのは数値のみですし、関数宣言は関数呼び出しの前に記述しますので注意してください。

※ 関数宣言の形式1
function 関数名()
{
  コマンドリスト
  return n ← 終了ステータスnを返して終了します
}

※ 関数宣言の形式2
関数名()
{
  コマンドリスト
  return n
}

※ 関数呼び出し
関数名 引数1 引数2 … 引数n ← 必要に応じて引数を指定します

例題

以降の例題では、シェルスクリプト内でどこでも使えるグローバル変数を英大文字の名前にしていますが、これはグローバル変数ということを明確にするためです。それ以上の意味はありません。

設定ファイルに記述したディレクトリパス名(絶対パス名)のバックアップを~/BACKUPに取得します。設定ファイルは第1引数で指定しますが、指定しない場合は~/mybin/ex60.confを使用します。(ex60.sh)

#!/bin/bash
# 機能  :  設定ファイルに記述したディレクトリパス名のバックアップを~/BACKUPに取得します。
#          設定ファイルは第1引数で指定しますが、指定しない場合は「~/mybin/ex60.conf」を使用します
# 作成  :  メリー
 
BACKUP_CONFIG_PATH=${1:-${HOME}/mybin/ex60.conf}       # 設定ファイルのパス名
BACKUP_DEST_PATH="${HOME}/BACKUP/"                     # バックアップファイル格納先のパス名
declare -a BACKUP_SOURCE_DIR                           # バックアップ対象ディレクトリの格納用配列
 
# バックアップ作業を行う
function ExecBackup()                  # バックアップ取得関数
{
    tar -C "$1" -cjf - .  > "$2"       # バックアップ取得
 
    return $?                          # バックアップの結果を戻す
}
 
# 設定ファイルの内容をBACKUP_SOURCE_DIRに取得
function GetConfig()                   # 設定ファイルの内容取得関数
{
    local path
    local -i index
 
    # 設定ファイルの有無をチェック
    if [[ ! -f "$BACKUP_CONFIG_PATH" ]]
    then
        echo "設定ファイルの${BACKUP_CONFIG_PATH}がありません。中止します。"
        exit 1;
    fi
 
    index=0
    for path in $(cat "$BACKUP_CONFIG_PATH")
    do
        # バックアップ対象ディレクトリの有無をチェック
        if [[ -d "$path" ]]
        then
            # バックアップ対象ディレクトリを格納
            BACKUP_SOURCE_DIR[$index]="$path"
            index=$(( $index + 1 ))
        else
            echo "バックアップ対象の${path}がありません。スキップします。"
        fi
    done
 
    # バックアップ対象ディレクトリの数をチェック
    if (( ${#BACKUP_SOURCE_DIR[*]} == 0 ))
    then
        echo "バックアップ対象ディレクトリがありません。中止します。"
        exit 2
    fi
 
    return 0
}
 
# 設定ファイルの内容をBACKUP_SOURCE_DIRに取得
GetConfig                              # 設定ファイルの内容取得関数の呼び出し
 
for path in ${BACKUP_SOURCE_DIR[*]}
do
    # バックアップ対象ディレクトリの最後のディレクトリ名を取得
    dir_name="${path##/*/}"
    # バックアップファイルのパス名を生成(YYMMDD_HHMMSS_ディレクトリ名.tar.bz2)
    backup_file="${BACKUP_DEST_PATH}$(date "+%Y%m%d_%H%M%S_${dir_name}.tar.bz2")"
 
    ExecBackup "$path" "$backup_file"  # バックアップ取得関数の呼び出し
    # バックアップ結果のチェック
    if (( $? == 0 ))
    then
        echo "${path}のバックアップを取得しました。"
    else
        echo "${path}のバックアップを取得できませんでした。"
    fi
done
 
echo "バックアップを終了します。"
$ cat ~/mybin/ex60.conf ← 設定ファイルの内容を表示します
/home/merry/WORK/DATA
/home/merry/WORK/DUMMY
/home/merry/WORK/PHOTO
/home/merry/WORK/SCRIPT
/home/merry/TMP/WORK/bookmarks.html
$
$ ex60.sh ← バックアップを実行します
バックアップ対象の/home/merry/WORK/DUMMYがありません。スキップします。
バックアップ対象の/home/merry/TMP/WORK/bookmarks.htmlがありません。スキップします。
/home/merry/WORK/DATAのバックアップを取得しました。
/home/merry/WORK/PHOTOのバックアップを取得しました。
/home/merry/WORK/SCRIPTのバックアップを取得しました。
バックアップを終了します。
$
$ ls -l ~/BACKUP ← バックアップの実行結果を確認します
合計 732
-rw-r--r-- 1 merry merry 676106  2月 19 09:52 20180219_095246_DATA.tar.bz2
-rw-r--r-- 1 merry merry  59131  2月 19 09:52 20180219_095246_PHOTO.tar.bz2
-rw-r--r-- 1 merry merry   5775  2月 19 09:52 20180219_095246_SCRIPT.tar.bz2
$
$ tar -tf ~/BACKUP/20180219_095246_SCRIPT.tar.bz2 ← バックアップファイルの内容を表示します
./
./loop.prg
./ex02.sh
./loop.c
./ex03.sh
./ex01.sh
$

6.8.3 関数宣言の分離

シェルスクリプトの機能を関数として宣言しておくと、別のシェルスクリプトでもその関数を使いたいということがあります。その場合、エディタで関数宣言の部分をコピーしても良いのですが、関数に変更が起きたときの処置が面倒です。そこで、複数のシェルスクリプトで共通に使われるような関数は共通関数として、1つのファイルにまとめて宣言しておくと便利です。

共通関数を使用するシェルスクリプトでは、共通関数を宣言しているファイルを取り込むことにより、共通関数を使用することが出来ます。ファイルの取り込みはsourceコマンドを使用します。

形式

sourceコマンドは引数で指定されたファイルをsourceコマンド実行プロセスと同じプロセスで実行させるためのコマンドです。

source 取り込むファイルのパス名 ← sourceの替わりに.(ドット)も使えます。引数の指定もできます

例題

前の例題の2つの関数(ExecBackupとGetConfig)を共通関数として、1つのファイル(ex62.sh)にまとめました。ex62.shは省略します。

標準入力から入力したディレクトリパス名のバックアップを~/BACKUPに取得します。入力した値は作業用ファイル(/tmp/ex61.conf)に出力し、設定ファイルとして使用します。(ex61.sh)

#!/bin/bash
# 機能  :   標準入力から入力したディレクトリパス名のバックアップを~/BACKUPに取得します。
#           入力した値は/tmp/ex61.confに出力し、設定ファイルとして使用します。
# 作成  :   メリー
 
source ex62.sh                         # 共通関数の取り込み
 
BACKUP_CONFIG_PATH="/tmp/ex61.conf"    # 設定ファイルのパス名
BACKUP_DEST_PATH="${HOME}/BACKUP/"     # バックアップファイル格納先のパス名
declare -a BACKUP_SOURCE_DIR           # バックアップ対象ディレクトリの格納用配列
 
echo "バックアップ対象ディレクトリの絶対パス名を入力してください。"
# 絶対パス名としてendが入力されるまで繰り返す
until [[ "$path" = "end" ]]
do
    read -p "絶対パス名 ==> " path
 
    if [[ "$path" != "end" ]]
    then
        # 入力した値を設定ファイルに追加出力
        echo "$path" >> $BACKUP_CONFIG_PATH
    fi
done
echo
 
# 設定ファイルの内容をBACKUP_SOURCE_DIRに取得
GetConfig
 
echo "バックアップを開始します。"
 
for path in ${BACKUP_SOURCE_DIR[*]}
do
    # バックアップ対象ディレクトリの最後のディレクトリ名を取得
    dir_name="${path##/*/}"
    # バックアップファイルのパス名を生成(YYMMDD_HHMMSS_ディレクトリ名.tar.bz2)
    backup_file="${BACKUP_DEST_PATH}$(date "+%Y%m%d_%H%M%S_${dir_name}.tar.bz2")"
 
    ExecBackup "$path" "$backup_file"
    # バックアップ結果のチェック
    if (( $? == 0 ))
    then
        echo "${path}のバックアップを取得しました。"
    else
        echo "${path}のバックアップを取得できませんでした。"
    fi
done
 
echo "バックアップを終了します。"
rm -f $BACKUP_CONFIG_PATH              # 設定ファイルを削除
$ ex61.sh
バックアップ対象ディレクトリの絶対パス名を入力してください。
絶対パス名 ==> /home/merry/WORK/PHOTO
絶対パス名 ==> /home/merry/SCRIPT
絶対パス名 ==> /home/merry/WORK/SCRIPT
絶対パス名 ==> end

バックアップ対象の/home/merry/SCRIPTがありません。スキップします。
バックアップを開始します。
/home/merry/WORK/PHOTOのバックアップを取得しました。
/home/merry/WORK/SCRIPTのバックアップを取得しました。
バックアップを終了します。
$