Yash 2 その 294: トラップ内での return 結果
POSIX の return 組込みの規定には昔から When return is executed in a trap action, the last command is considered to be the command that executed immediately preceding the trap action.
と書いてあって、exit 組込みと同様に、トラップ内で無引数で return 組込みを呼んだらトラップに入る直前の終了ステータスが return の終了ステータスになることになってゐる。が、果たしてこれは合理的な挙動なのだらうか?
Return 組込みの規定では If the shell is not currently executing a function or dot script, the results are unspecified.
となってゐるので、return 組込みを用ゐてトラップから脱出することは想定されてゐないはずである。つまり、トラップ内で return 組込みを使ふことがあったとしても、それはトラップの中で呼ばれてゐる函数 (またはドットスクリプト) からの脱出だけが可搬性のある動作である。だとすると、なぜ return 組込みの結果を決めるために函数 (またはドットスクリプト) の外で発生してゐるトラップの実行状況を気にする必要があるのだらうか? 函数の中で return 組込みを呼んだら、その函数の中で return 組込みの直前の終了ステータスを (トラップの中かどうかに関係なく) 返すのが自然だらう。
POSIX に字義通りに従ふのなら、トラップ内で呼ばれる函数の中で直前のコマンドの終了ステータスを使って函数から脱出したい場合は return 組込みを無引数で呼ぶのではなく return $?
とした方が良いといふことになる。函数がトラップ内で呼ばれるかどうかによって return
と return $?
を使ひ分けるのは手間なので、return 組込みを無引数で呼ぶのは常にやめておけといふことになってしまふ。
実際にいくつかのシェルで実験してみると、以下のコードで echo が出力する値はシェルによってバラバラの結果になる。
trap '(exit 1); exit' INT
trap '(exit 2); f; echo $?' EXIT
f() { (exit 3); return; }
kill -INT $$
例へば mksh-59c は 0 になるのに対し、bash-5.2.15 と yash-2.54 は 1、busybox-1.36.0 と dash-0.5.11.5 と ksh-93u+m/1.0.4 と zsh-5.9 は 3 になる。上記で議論した自然な動作となってゐる (すなはち、トラップ内かどうかを無視して直前の終了ステータスを返す) のは 3 を出力する dash や ksh である。0 を出力する mksh は INT トラップに入る前の終了ステータスを返してゐる。1 を出す bash と yash は EXIT トラップに入る前の終了ステータスを返してゐる。
ここで別の疑問が発生する。INT トラップの中で EXIT トラップが実行されてゐる時、the command that executed immediately preceding the trap action.
とはどちらのトラップを指すのか? POSIX は何も語ってゐない。
また、yash-2.54 には EXIT トラップの中で return 組込みを無引数で呼んだ場合にそれを考慮してをらず、シグナルのトラップの場合と一貫性がない。例へば以下の二つのスクリプトは前者が 3、後者が 0 を出力する。
trap '(exit 2); f; echo $?' EXIT
f() { (exit 3); return; }
exit
trap '(exit 1); f; echo $?' INT
f() { (exit 3); return; }
kill -INT $$
exit
何にせよ、可搬性のあるシェルスクリプトを書かうとする人にとってはつらい状況になってゐる。POSIX の規定は字義的に解釈すると合理的ではなく、また曖昧さもあり、現実にそれに従ってゐるシェルと従ってゐないシェルとがある。結論としては面倒だが毎回 $?
を渡すやうにするのが安全といふことになる。
Yash の動作をどうすべきかも難しい問題である。トラップの種類によって動作が異なるのは直すべき気がするが……。しかし二重のトラップの中での動作の方針を決める良い基準は見当たらない。(POSIX 準拠モードがオフの時はトラップを無視して常に直前の終了ステータスを返すのはありかもしれない。)
| 固定リンク
コメント
これに関しては kre が https://austingroupbugs.net/view.php?id=1602 で長いレポートを書いてゐる様だ。(まだ読んでゐない)
投稿: まじかんと | 2023年6月 4日 (日) 13時29分
どのみち、他のシェルの動作がいまいちな状況では yash の動作だけを "改良" しても大して意味はないのかもしれないな
投稿: まじかんと | 2023年6月 4日 (日) 14時19分
「return 組込みを用ゐてトラップから脱出することは想定されてゐないはずである。」と書いたが、函数の中でトラップが発動してゐる場合には return でトラップを貫通して函数から脱出できるシェルは多いやうだ。(今の yash はさうではない)
投稿: まじかんと | 2023年6月 4日 (日) 14時44分