「set -e」は何をするもので、なぜそれが危険とみなされるのでしょうか?


147

この質問はインタビュー前のクイズに出てきて、私を夢中にさせています。誰でもこれに答えて私を安心させることができますか?クイズには特定のシェルへの参照はありませんが、ジョブの説明はUNIX sa用です。ここでも質問は単純です...

「set -e」は何をするもので、なぜそれが危険とみなされるのでしょうか?


回答:


154

set -e サブコマンドまたはパイプラインがゼロ以外のステータスを返す場合、シェルを終了します。

インタビュアーがおそらく探していた答えは次のとおりです。

init.dスクリプトを作成するときに「set -e」を使用するのは危険です。

http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 から-

init.dスクリプトでset -eを使用する場合は注意してください。正しいinit.dスクリプトを作成するには、デーモンが既に実行されているか、init.dスクリプトを中止せずに既に停止している場合、さまざまなエラー終了ステータスを受け入れる必要があります。init.dスクリプトの場合、多くの場合、set -eを使用せず、代わりに各コマンドの結果を個別に確認する方が簡単です。

これは、インタビュアーの観点からの有効な質問です。なぜなら、サーバーレベルのスクリプティングと自動化に関する実用的な知識を候補者が評価するからです。


いい視点ね。技術的にはエラーである可能性のある何かについてブートプロセスを停止しますが、スクリプト全体が停止することはありません
Matt

stackoverflow.com/questions/78497/…には同意しますが、このスタイルは、ターゲットワークロードを実行する前に、短いinitチェックやロギング、または他の環境モンキーパッチにbashに適していると思います。初期化スクリプト。
ジョスフェリー

33

からbash(1)

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ││  list
                  except  the  command  following  the final && or ││, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.

残念ながら、「スクリプトの残りの部分は実行されない」または「おそらく実際の問題をマスクする可能性がある」以外は、なぜ危険なのかを考えるほど創造的ではありません。


1
本当の問題や他の問題を隠して、ええ、私はそれを見ることができたと思います...私はそれが危険であるという例を
見つけるの

のマンページがありsetます!私はいつもで終わるbuiltin(1)。ありがとう。
ジャレッドベック

どのように私はのためのドキュメントがあることを知っているだろうsetにbefoundすることですman 1 bash?そして、巨大なマニュアルページでキーワードの-eオプションをどのように見つけることができますsetか?/-eここで検索することはできません
phil294

@Blauhirn使用help set
phil294

19

set -eスクリプトのさまざまなセクションでオンとオフを切り替えることができることに注意してください。スクリプト全体の実行中にオンにする必要はありません。条件付きで有効にすることもできます。とはいえ、自分でエラー処理を行っている(または行っていない)ため、私は使用しません。

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status

また、注目に値するのは、特定の状況でtrapどのようにset -e機能するかを説明するコマンドのBashのマニュアルページセクションです。

失敗したコマンドがwhileキーワードまたはuntilキーワードの直後のコマンドリストの一部、ifステートメントのテストの一部、&&または⎪⎪リストで実行されたコマンドの一部、またはコマンドの場合、ERRトラップは実行されません戻り値は!によって反転されています。これらは、errexitオプションが従うのと同じ条件です。

そのため、ゼロ以外のステータスが終了を引き起こさない条件がいくつかあります。

危険なのは、いつset -e出てくるのか、いつ出ないのかを理解せず、何らかの無効な仮定の下で誤ってそれに頼ることにあると思います。

BashFAQ / 105も参照してください。なぜ-eを設定(または-o errexitを設定、またはERRをトラップ)しても期待どおりの結果が得られないのはなぜですか?


質問から少し外れていますが、この答えはまさに私が探しているものです:スクリプトのほんの一部でそれをオフにします!
ステファンBijzitter

13

これは就職の面接のためのクイズであることに留意してください。質問は現在のスタッフによって書かれたものであり、間違っている可能性があります。これは必ずしも悪いことではなく、誰もが間違いを犯し、インタビューの質問は多くの場合、レビューなしで暗い隅に座り、インタビュー中にのみ出てきます。

「set -e」が「危険」とみなすものを何もしないことは完全に可能です。しかし、その質問の著者は、「set -e」が彼ら自身の無知または偏見のために危険であると誤って信じるかもしれません。たぶん彼らはバグのあるシェルスクリプトを書き、それが恐ろしく爆撃し、実際には適切なエラーチェックを書くことを怠っていたのに、「set -e」が原因だと誤って考えていました。

私は過去2年間でおそらく40件の就職面接に参加しており、面接官は時々間違った質問をしたり、間違った答えを持っています。

または、それは不完全な質問ですが、完全に予想外ではありません。

または、これは良い説明かもしれません:http : //www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html


1
+1私は同じ船に乗っており、私が扱ってきた多くの「技術的な」インタビューは、少なくともわずかに見当違いでした。
cpbills

1
ワオ。debianリストでよく見つけてください。面接の質問の無知のために+1。私は自分が正しいと100%確信していたので、一度ネットバックの答えを主張したことを覚えています。彼らはノーと言いました。家に帰ってグーグルで調べたところ、正しかった。
エゴリー

9

set -e スクリプトでbashに、ゼロ以外の戻り値が返されたときに終了するように指示します。

何かに対するアクセス許可を開いていない限り、それがどのように迷惑で、バグがあり、危険かはわかりません。また、再び制限する前に、スクリプトは死にました。


それは面白い。途中で死ぬスクリプトで開かれたアクセス許可については考えていませんでしたが、危険と見なされていることがわかりました。このクイズの楽しい部分は、manやgoogleなどの参考資料を使用できず、完全に回答できない場合はまったく回答しないことです。
エゴリー

6
それは馬鹿げている、私はこの雇用主を再考するだろう...冗談(のような)。強力な知識ベースを持つことは良いことですが、IT業務の半分は情報を見つけるために/ where /を知っています。ちなみに、/ no / use forを参照してください。set -e実際、スクリプトのエラーチェックを無視するのは怠lazだと言われています。多くの場合、それらのスクリプトはどのように見えますか。必然的に維持する必要があります
...-cpbills

優れた点@cpbills。また、スクリプトでset -eを使用することもありません。おそらく、それが私を困惑させた理由です。いくつかは本当に良いものもあれば、このような本当に奇抜でランダムなものもあるので、すべての質問を投稿したいと思います。
エゴリー

私は、多くのシステムcronジョブでそれを見てきました...
アンドリュー

8

set -e特定の条件下を除き、ゼロ以外の終了コードが検出された場合、スクリプトを終了します。いくつかの言葉でその使用の危険性を要約すると:それは人々がそれを考えるように振る舞わない。

私の意見では、互換性のためだけに存在し続ける危険なハックと見なされるべきです。このset -eステートメントは、エラーコードを使用する言語のシェルを、例外のような制御フローを使用する言語に変換するものではなく、単にその動作をエミュレートしようとするものです。

グレッグウーレッジは、次の危険性について多くのことを述べていset -eます。

2番目のリンクには、の直感的で予測できない動作のさまざまな例がありset -eます。

の直感に反する動作の例set -e(上記のWikiリンクから取得したもの):

  • セット-e
    x = 0
    x ++
    echo "xは$ xです"
    上記により、シェルスクリプトは早まって終了しlet x++ます。これは、0を返すためです。これは、letキーワードによって偽の値として扱われ、ゼロ以外の終了コードになります。set -eこれに気付き、サイレントでスクリプトを終了します。
  • セット-e
    [-d / opt / foo] && echo "警告:fooは既にインストールされています。上書きします。" >&2
    echo "fooをインストールしています..."
    

    上記は期待どおりに動作し、/opt/foo既に存在する場合は警告を出力します。

    セット-e
    check_previous_install(){
        [-d / opt / foo] && echo "警告:fooは既にインストールされています。上書きします。" >&2
    }
    
    check_previous_install
    echo "fooをインストールしています..."
    

    上記は、1行が関数にリファクタリングされているという唯一の違いにもかかわらず、/opt/foo存在しない場合は終了します。これは、元々set -e動作していたという事実がの動作に対する特別な例外だからです。a && bゼロ以外を返す場合、無視されset -eます。ただし、関数であるため、関数の終了コードはそのコマンドの終了コードと等しくなり、ゼロ以外を返す関数はスクリプトをサイレント終了します。

  • セット-e
    IFS = $ '\ n'読み取り-d '' -r -a config_vars <config
    

    上記はconfig_varsファイルから配列を読み込みますconfig。作成者が意図しているように、config行方不明の場合はエラーで終了します。著者は意図しないかもしれないのでconfig、改行で終わらないなら黙って終了します。set -eここで使用されなかった場合config_vars、改行で終了したかどうかに関係なく、ファイルのすべての行が含まれます。

    Sublime Text(および改行を誤って処理する他のテキストエディター)のユーザーは注意してください。

  • セット-e
    
    should_audit_user(){
        ローカルグループgroups = "$(groups" $ 1 ")"
        $ groupsのグループ。行う
            if ["$ group" = audit]; その後、0を返します。fi
        やった
        1を返す
    }
    
    should_audit_user "$ user"; それから
        ロガー「ブラー」
    fi
    

    ここでの作成者$userは、何らかの理由でユーザーが存在しない場合、groupsコマンドが失敗し、ユーザーに監査されていないタスクを実行させる代わりにスクリプトが終了することを合理的に期待するかもしれません。ただし、この場合、set -e終了は有効になりません。$user何らかの理由で見つからない場合、スクリプトは終了せずに、should_audit_user関数set -eは無効であるかのように誤ったデータを返すだけです。

    これは、ifステートメントの条件部分から呼び出された関数に適用されます。ネストの深さに関係なく、定義されている場所に関係なく、set -e再度その内部で実行しても。ifいつでも使用set -eすると、条件ブロックが完全に実行されるまでの効果が完全に無効になります。作者がこの落とし穴に気付いていない場合、または関数が呼び出される可能性のあるすべての状況で呼び出しスタック全体を知らない場合、バグのあるコードを記述し、提供さset -eれる誤ったセキュリティ感覚を少なくとも部分的に非難。

    作成者がこの落とし穴を完全に認識している場合でも、回避策は、なしset -eで書くのと同じ方法でコードを書くことです。作者は、あたかも有効になってset -eいないかのように手動でエラー処理コードを書かなければならないだけでなく、set -eそれらが存在することで、それらを欺く必要がないと思わせるかもしれません。

のさらなる欠点set -e

  • ずさんなコードを奨励します。エラーハンドラは、失敗したものが何らかの適切な方法でエラーを報告することを期待して、完全に忘れられています。ただし、let x++上記のような例では、そうではありません。スクリプトが予期せず終了した場合、通常はサイレントに実行され、デバッグが妨げられます。スクリプトが死なず、予想していた場合(前の箇条書きを参照)、より微妙で陰湿なバグが手元にあります。
  • それは人々を誤った安心感に導きます。再びif-condition箇条書きを参照してください。
  • シェルが終了する場所は、シェル間またはシェルバージョン間で一貫していません。古いバージョンのbashでは動作が異なるためset -e、これらのバージョン間で動作が異なるスクリプトを誤って作成する可能性があります。

set -e論争の多い問題であり、周囲の問題に気づいている人がそれに対して推奨している人もいれば、落とし穴を知るのが活発な間は注意を払うだけの人もいます。set -eすべてのスクリプトでエラー状態のキャッチオールとしてお勧めするシェルスクリプトの初心者は多くいますが、実際にはそのようには動作しません。

set -e 教育に代わるものではありません。


2

もうスクリプトの流れを制御できないので、危険だと思います。スクリプトが呼び出すコマンドのいずれかがゼロ以外を返す限り、スクリプトは終了できます。したがって、必要なことは、コンポーネントの動作または出力を変更するものを実行するだけで、メインスクリプトを終了することができます。それはもっとスタイルの問題かもしれませんが、間違いなく結果をもたらします。あなたのメインスクリプトが何らかのフラグを設定することになっていて、それが早期に終了したためにそうしなかったらどうしますか?フラグが存在すると想定した場合、または予期しないデフォルト値または古い値で作業しているとシステムの残りの部分に障害が発生することになります。


この答えに完全に同意します。本質的に危険ではありませんが、予期せずに早期終了する可能性があります。
pboin

1
これが私に思い出させたのは、プログラムに単一のエントリポイント/単一の出口ポイントがないために、常に私の割り当てからポイントを引き出していた私のC ++教授です。私はそれが純粋に「原則」だと思っていましたが、このセット-eビジネスは、物事が完全に制御不能になる方法と理由を間違いなくデモします。最終的にプログラムは制御と決定論に関するものであり、早期終了では両方を放棄します。
マーチン

0

個人的に噛まれた@Marcinの答えのより具体的な例として、rm foo/bar.txtスクリプトのどこかに線があったと想像してください。通常、foo/bar.txt実際に存在しなければ大した問題ではありません。ただし、set -e現在では、スクリプトはそこで早く終了します!おっと。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.