errexit(`set -e`) を修正するシェルを書いた人はいますか?

errexit(`set -e`) を修正するシェルを書いた人はいますか?

errexit( set -e) は、単純なスクリプトをより強力にする方法として提案されます。しかし、より複雑なスクリプト(特に関数)でその動作を見た人ならば、一般的にこれがひどい落とし穴であることに同意します。サブシェルにも非常に似た問題があります。例えば、https://stackoverflow.com/questions/29926013/exit-subshel​​l-on-error

(
set -o errexit
false
true
) && echo "OK" || echo "FAILED";

ここでトラップは、シェルが「FAILED」の代わりに「OK」を表示することです。 [*]

特定の状況での動作はset -e歴史的に不幸な方法で変わりましたが、最新のディストリビューションで利用可能なシェルはPOSIXシェル標準に準拠していると主張し、テストによれば、次のすべてが同じ動作(「OK」)を持っていることがわかりました。

  • bash-4.4.19-2.fc28.x86_64(フェドラハット)
  • busybox-1.26.2-3.fc27.x86_64(フェドラハット)
  • dash 0.5.8-2.4(Debian 9)
  • zsh 5.3.1-4+b2(Debian 9)
  • posh 0.12.6+b1(Debian 9)
  • ksh 93u+20120801-3.1(Debian 9).

質問

  1. 上記のコードをsh印刷する以外に、POSIXと同様の機能を持つシェルを作成できない技術的な理由はありますか?FAILED

    falseを返す最上位式が致命的と見なされる set -eように変更される可能性があります。&&https://serverfault.com/a/847016/133475 grepで使用できるより良いイディオムがあることを願っています。

    set -e
    
    # print matching lines, but it's not an error if there are no matches in the file
    (
    set +e
    grep pattern file.txt
    RET=$?
    [[ $RET == 0 || $RET == 1 ]] || exit $RET
    )
    

    let私はまた、数値を暗黙的にtrue / false終了状態に強制することを避けるために、算術ステートメント(組み込み)が何らかの方法でオーバーライドされると仮定しています。

  2. これを行うことができるシェルの例はありますか?

    Bourneの構文と異なるものを見るのは大丈夫です。短い「接続」スクリプトを書くとき、私は簡潔なことに興味がありますが、ここではエラーを検出するための簡潔な戦略もあります。&&誤ってドア区切り文字を省略すると、エラーが自動的に無視されることを意味する場合は、ドア区切り文字として使用するのが好きではありません。


[*]編集する。これは完璧な例ではないかもしれません。議論によると、より良い例は、set -o errexit上記のサブシェルを移動することです。

(false; echo foo) || echo bar; echo \ $?AFAICT これは表のテストケースに非常に似ています。こここれは、元のBourneシェルとその子孫のほとんどに「foo」(そして「0」)が表示されることを意味します。 "hist.ash"とbash 1.14.7は例外です。

set -eサブシェルに - - を追加すると、(set -e; false; echo foo) || echo bar; echo \ $?「SVR4 sh sun5.10」とdash-0.3.4という2つの追加例外があります。

私の質問の精神によると、set -eサブシェルに入ることは注意を気を散らすと思います。私は主にset -eスクリプトの上部に使用されるイディオムに興味があり、これはサブシェルにも当てはまります(すでにPOSIXの動作です)。

Jörg Schillingは、もともとUnixのBourneシェルが私がこの質問に使用した例について「FAILED」を印刷すると言いました。Healyツールの「osh」、その結果は2018年6月11日発表当時検証されました。おそらく、これは1)set -eサブシェルの内部にあるものと2)「SVR4 sh sun5.10」に基づいているかもしれません。 「osh」は「OpenSolarisソースに基づいているため、SVR4とSVID3」です。あるいは、&& echo "OK"サブシェル間に追加すると、ひどい追加の違いが発生する可能性があります|| echo "FAILED"

私は狡猾な殻が私の質問に答えないと思います。関数にサブシェルを使用できます(サブシェルで関数を実行する(代わりに使用)。ただし、サブシェルを使用すると、グローバル変数を変更できないという制限があります。少なくとも普遍的なコーディングスタイルだと擁護する人は見たことがない。{set -e

ベストアンサー1

これPOSIX規格(より- 金利errexitシェルオプションについては、Bashマニュアルよりも明確です。

このオプションを有効にすると、コマンドが失敗した場合(シェルエラーの結果に記載されている理由またはゼロより大きいシャットダウン状態を返す場合)、シャットダウン特殊組み込みユーティリティなしで実行されたかのように、シェルはすぐにシャットダウンする必要があります。以下の状況を除くすべての引数:

  1. 複数のコマンドパイプラインで単一のコマンドが失敗しても、シェルは終了しないでください。パイプライン自体のエラーのみが考慮されます。

  2. while、Until、if、またはelifの予約語(!で始まるパイプ)の後にある複合リストを実行するときは、-e設定を無視する必要があります。予約語またはAND-ORリストの最後の項目を除くすべてのコマンド。

  3. -eが省略されたときに終了状態が失敗の結果である場合、-eはサブシェルコマンドではなく複合コマンドには適用されません。

この要件は、シェル環境と各サブシェル環境に別々に適用されます。たとえば、

set -e; (false; echo one) | cat; echo two

false コマンドを使用すると、echo 1 を実行せずにサブシェルが終了します。ただし、パイプ(false; echo one)|catの終了状態は0なので、echo 2が実行されます。

明らかに、サンプルコードは次の擬似コードと同じです。

( LIST; ) && COMMAND || COMMAND

終了ステータスリスト 次の理由でゼロです。

  • シェルerrexitオプションは無視されます。

  • 戻り状態は、指定された最後のコマンドの終了状態です。リスト、ここtrue

したがって、ANDリストの2番目の部分を実行してくださいecho "OK"

おすすめ記事