web-dev-qa-db-ja.com

スクリプトを終了しない終了

exitは、エラーが呼び出されてもスクリプトを終了しません。

出力

Error: Could not resolve localhost
after exit

脚本

#!/bin/sh

resolve_ip (){
    if [ -z "$1" ]; then
        Host="localhost"
        ip=$(Dig +short myip.opendns.com @resolver1.opendns.com)
    else
        Host="$1"
        ip=$(Dig +short $1)
    fi

    if [ -z "$ip" ]; then
        error "Could not resolve $Host"
    fi

    echo "$ip"
}

error (){
    (>&2 echo "Error: $1")
    exit 1
}

master_Host='google.com'

if [ "$(resolve_ip)" = "$(resolve_ip $master_Host)" ]; then
    error "some error"
fi

echo "after exit"
exit
7
clarkk

exitは現在のシェルプロセスを終了します¹。

$(resolve_ip)では、resolve_ipがサブシェルプロセスで実行されています。

できるよ:

my_ip=$(resolve_ip) || exit
master_ip=$(resolve_ip "$hostname") || exit
if [ "$my_ip" = "$master_ip" ]; ...

サブシェルがゼロ以外の終了ステータスで終了したときにメインシェルが終了する(サブシェルと同じ終了コードで)。

また、resolve_ipはサブシェル環境で実行されるため、$ipおよび$Host変数は、そのサブシェルが戻った後は存続しません。

(...)(>&2 echo "Error: $1")もサブシェルを開始することに注意してください。 stderrが壊れたパイプであり、エラーメッセージを書き込むとechoが組み込まれているため、メインシェルプロセスにSIGPIPE配信が発生する場合を除き、ここでは実際には必要ありません。

ここでは、標準出力を介して出力を返す代わりに、ユーザーが指定した変数に保存することで出力を返すことができます。

resolve_ip (){ # args: ip_var [Host]
    if [ "$#" -eq 1 ]; then
        Host=localhost
        eval "$1="'$(Dig +short myip.opendns.com @resolver1.opendns.com)'
    else
        Host=$2
        eval "$1="'$(Dig +short "$2")'
    fi

    if eval '[ -z "${'"$1"'}" ]'; then
        error "Could not resolve $Host"
    fi
}

# ...

resolve_ip my_ip
resolve_ip master_ip "$hostname"

if [ "$my_ip" = "$master_ip" ]; ...

¹厳密に言えば、サブシェル環境は子プロセスで実装する必要はなく、ksh93などの一部のシェルは最適化としては機能しませんが、exitはメインシェルではなくサブシェルのみを終了します。ただし、ksh93には、サブシェル環境を含まない${ ...; }形式またはコマンド置換があるため、exitはメインシェルを終了します。

18