web-dev-qa-db-ja.com

サブシェルからシェルスクリプトを終了する

このスニペットを考えてみましょう:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

通常、funcが呼び出されると、スクリプトが終了しますが、これは意図された動作です。ただし、次のようなサブシェルで実行された場合

result=`func`

スクリプトを終了しません。これは、呼び出しコードが関数の終了ステータスを毎回チェックする必要があることを意味します。これを回避する方法はありますか?これは何ですかset -eは?

32
Ernest A C

あなたcould元のシェルを殺す(kill $$exitを呼び出す前に、おそらくそれでうまくいきます。だが:

  • 私にはかなり醜いようです
  • そこに2番目のサブシェルがある場合、つまり、サブシェル内でサブシェルを使用すると壊れます。

代わりに、 Bash FAQの値を返すいくつかの方法 のいずれかを使用できます。それらのほとんどは残念ながらそれほど素晴らしいものではありません。関数を呼び出すたびにエラーのチェックでスタックするだけかもしれません( -eには多くの問題があります )。それか、Perlに切り替えてください。

10
derobert

たとえば、終了ステータス77は、任意のレベルのサブシェルを終了することを意味し、

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

ERRトラップと組み合わせたset -Eは、独自のエラー処理を定義できるという点で、set -eの改良版と少し似ています。

Zshでは、ERRトラップは自動的に継承されるため、set -Eは必要ありません。TRAPERR()関数としてトラップを定義し、$functions[TRAPERR]のようにfunctions[TRAPERR]="echo was here; $functions[TRAPERR]"を介してトラップを変更することもできます。

38

kill $$の代わりに、kill 0を試すこともできます。ネストされたサブシェルの場合に機能します(すべての呼び出し元とサイドプロセスがシグナルを受信します)…しかし、それでも残忍で醜いです。

7

(Bash固有の回答)Bashには例外の概念はありません。ただし、最も外側のレベルでset -o errexit(または同等の:set -e)を使用すると、コマンドが失敗すると、サブシェルがゼロ以外の終了ステータスで終了します。これが、サブシェルの実行に関する条件なしの入れ子になったサブシェルのセットである場合、スクリプト全体を効果的に「ロールアップ」して終了します。

さまざまなbashコードのビットを大きなスクリプトに含めようとする場合、これは注意が必要です。 bashの1つのチャンクは、それ自体で問題なく機能しますが、errexitの下で(またはerrexitなしで)実行されると、予期しない動作をします。

 [192.168.13.16(f0f5e19e)〜22:58:22]#bash -o errexit /tmp/foo
問題が発生しました
 [192.168.13.16(f0f5e19e)〜22 :58:31]#bash /tmp/foo
問題が発生しました
しかし、とにかくここに来ました
 [192.168.13.16(f0f5e19e)〜22:58:37]#猫/tmp/foo
#!/bin/bash
stop(){
 echo "$ {1}" 
 exit 1 
} 
 
偽の場合;次に、
 echo "foo" 
 else 
(
 stop "something got bad" 
)
 echo "But we here hereとにかく "
 fi 
 [192.168.13.16(f0f5e19e)〜22:58:40]#
0
Brian Chrisman

これを試して ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "Shell..."
func $1

echo "subshell..."
result=`func $1`

echo "Shell..."
echo "result=$result"

私が得る結果は...

# test_exitsubshell true
Shell...
foo
subshell...
Shell...
result=foo
# test_exitsubshell false
Shell...
something went wrong

ノート

  • ifテストがtrueまたはfalseになるようにパラメーター化(2回の実行を参照)
  • ifテストがfalseの場合、サブシェルに到達することはありません。
0
DocSalvager