トラップはサブシェルに継承されますか?

トラップはサブシェルに継承されますか?

次のスクリプトを試しました。

#!/bin/bash
trap 'echo "touching a file" && touch $FILE' EXIT

foo1(){
        echo "foo1"
}
foo(){
        echo "foo"
        export FILE=${FILE:-/tmp/file1}
}
(foo1)
foo

上記のスクリプトの出力は次のとおりです。

[root@usr1 my_tests]# ./test.sh
foo1
foo
touching a file

foo1しかし、私はトラップがサブシェルから呼び出される出口でも呼び出されると予想しました。

  • これが期待されるか。
  • trapサブシェルに継承されますか?
  • それでは、どのような状況でtrapサブシェルに継承されますか?

答え1

トラップハンドラはサブシェルから継承されません。これはPOSIXで指定:

無視されないトラップは、サブシェルに入るとデフォルトの動作に設定されます。

無視された信号(trap '' SIGFOO)は、サブシェル(およびシェルによって開始された外部プログラム)ではまだ無視されます。

答え2

trapサブシェルには伝播されませんが、一部の方法では、サブシェルが親シェルにトラップを報告できるようにし、他の方法ではそうではありません。 bashを使ってmacOSでいくつかのテストを行いました。

GNU bash、バージョン 4.4.12(1)-リリース(x86_64-apple-darwin16.3.0):

trap 'echo hello' EXIT
trap # trap -- 'echo hello' EXIT
echo "$(trap)" # trap -- 'echo hello' EXIT
trap | cat # trap -- 'echo hello' EXIT
(trap) | cat # trap -- 'echo hello' EXIT
cat < <(trap) # empty
cat <<< "$(trap)" # empty
bash -c 'trap' # empty
trap & # trap -- 'echo hello' EXIT

GNU bash、バージョン 3.2.57(1)-リリース(x86_64-apple-darwin16):

trap 'echo hello' EXIT
trap # trap -- 'echo hello' EXIT
echo "$(trap)" # trap -- 'echo hello' EXIT
trap > >(cat) # trap -- 'echo hello' EXIT
trap | cat # empty
(trap) | cat # empty
cat < <(trap) # empty
cat <<< "$(trap)" # empty
bash -c 'trap' # empty
trap & # empty

trap_output="$(trap)"これがトラップ出力に役立つことに注意してください。これがうまくいかない場合trap >trap_output_fileは、ファイルに出力してから(fifoは機能しませんbash 3.2.57)、再読み込み以外の方法を考えることはできません。trap_output="$(<trap_output_file)"

fifo は空なbash 3.2.57ので動作しませんが、そうではありません。trap &bash 3.2.57bash 4.4.12

GNU bash、バージョン 4.4.12(1)-リリース(x86_64-apple-darwin16.3.0):

mkfifo /tmp/fifo; trap >/tmp/fifo & trap_output=$(</tmp/fifo); rm -f /tmp/fifo; echo "$trap_output"
# trap -- 'echo hello' EXIT

mkfifo /tmp/fifo; trap_output=$(</tmp/fifo) & trap >/tmp/fifo; rm -f /tmp/fifo; echo "$trap_output"
# empty because trap_output=$(</tmp/fifo) sets the variable in a subshell

GNU bash、バージョン 3.2.57(1)-リリース(x86_64-apple-darwin16):

mkfifo /tmp/fifo; trap >/tmp/fifo & trap_output=$(</tmp/fifo); rm -f /tmp/fifo; echo "$trap_output"
# empty because trap >/tmp/fifo & is empty since it uses trap &

mkfifo /tmp/fifo; trap_output=$(</tmp/fifo) & trap >/tmp/fifo; rm -f /tmp/fifo; echo "$trap_output"
# empty because trap_output=$(</tmp/fifo) sets the variable in a subshell

答え3

trap定義はサブシェルに伝播されません。

識別方法:

trap "echo bla" 1 2 3

(trap)

答え4

サブシェルを呼び出すと、親trapトラップが表示されますが、サブシェルでは実行されません。サブシェルに新しいトラップを設定できます。

これをテストするには、次のコードを使用してスクリプトを生成して実行し、ctrl+を実行しますc

#!/usr/bin/bash
function sub { echo sub_before: `trap`; trap 'echo kill_sub; exit' SIGINT; echo sub_after: `trap`; while :; do sleep 1; done; }
function parent { echo parent_before: `trap`; trap 'pkill -2 -P $$; echo kill_parent; exit' SIGINT; echo parent_after: `trap`; sub & wait; }
parent

両方のトラップが実行されていることがわかります。

(Bash 5.0.17)

関連情報