bashスクリプトでset -eはどういう意味ですか?


713

Debianアーカイブ(.deb)ファイルからパッケージが解凍される前にスクリプトが実行するこのpreinstファイルの内容を調査しています。

スクリプトには次のコードがあります。

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

私の最初のクエリは行についてです:

set -e

スクリプトの残りの部分はかなり単純だと思います。Debian/ Ubuntuパッケージマネージャーがインストール操作を実行しているかどうかをチェックします。インストールされている場合は、アプリケーションがシステムにインストールされたばかりかどうかを確認します。含まれている場合、スクリプトは「MyApplicationName is is installed」というメッセージを出力しreturn 1て終了します(つまり、「エラー」で終了しますね)。

ユーザーがDebian / Ubuntuパッケージシステムにパッケージのインストールを要求している場合、スクリプトは2つのディレクトリも削除します。

これは正しいですか、それとも何か不足していますか?



46
googleでこれを見つけることができなかった理由:クエリの-eは否定として解釈されます。次のクエリを試してください:bash set "-e"
Maleev 2014年

3
@twalberg私が同じ質問をしたとき、私は見ていたman set
Sedat Kilinc

4
あなたはそれをオフにする方法を探しているなら、プラスプレフィックスにダッシュをスワップ:set +e
トムSaleeba

@twalbergですが、実際の人に尋ねるのは、ロボットからリクエストを出すよりもはるかに興味深いです;-)。
vdegenne

回答:


797

からhelp set

  -e  Exit immediately if a command exits with a non-zero status.

しかし、それは一部の人(bash FAQとirc freenode #bash FAQの作者)は悪い習慣だと考えています。使用することをお勧めします:

trap 'do_something' ERR

do_somethingエラーが発生したときに関数を実行します。

http://mywiki.wooledge.org/BashFAQ/105を参照してください


14
「コマンドがゼロ以外のステータスで終了したらすぐに終了する」と同じセマンティクスが必要な場合、do_somethingはどうなりますか?
CMCDragonkai

71
trap 'exit' ERR
chepner 2014年

12
ERRトラップはあなたが機能を持っている、その場合、シェルの機能によって継承されていない set -o errtraceか、set -Eあなたは一度だけトラップを設定することを可能にし、グローバルに適用されます。
イケー

31
trap 'exit' ERRが異なるのset -e
アンディ

22
それが悪い習慣であるなら、なぜそれがDebianパッケージで使用されているのですか?
phuclv 2016

98

set -eコマンドまたはパイプラインにエラーがある場合、スクリプトの実行を停止します。これは、デフォルトのシェルの動作(スクリプトのエラーを無視する)の反対です。help set端末を入力して、この組み込みコマンドのドキュメントを参照してください。


44
パイプラインの最後のコマンドにエラーがある場合にのみ、実行を停止します。set -o pipefail前のコマンドのいずれかがゼロ以外のステータスで終了した場合にパイプラインコマンドの戻り値がゼロ以外になるようにエラーを伝播するために使用できるBash固有のオプションがあります。
Anthony Geoghegan 2015年

2
これは、パイプラインの最初のゼロ以外の(つまり、エラーのある)コマンドの終了ステータスが最後まで伝播される-o pipefailことだけを意味することに注意してください。パイプライン内の残りのコマンドは、でも実行されます。例えば:、ここで成功したが、当てにならないコマンドを表し、印刷され、および、しかしで、まだ印刷され、今のコマンドを表すサイレントerroring 終了する前に。-o errexitset -o errexitecho success | cat - <(echo piping); echo continuesecho successsuccesspipingcontinuesfalse | cat - <(echo piping); echo continuesfalsepiping
bb010g

55

通りのbash -セットビルトイン場合マニュアル、-e/ errexitセットされ、シェル出口が直ちに場合、パイプライン単一成る単純なコマンドリストまたは化合物コマンドが非ゼロのステータスを返します。

デフォルトでは、pipefailオプションが有効になっていない限り(デフォルトでは無効になっています)、パイプラインの終了ステータスはパイプラインの最後のコマンドの終了ステータスです。

その場合、最後の(右端の)コマンドのパイプラインの戻りステータスは、ゼロ以外のステータスで終了するか、すべてのコマンドが正常に終了した場合はゼロ。

終了時に何かを実行したい場合trapは、たとえばを定義してみてください。

trap onexit EXIT

どこonexitのようなその下に簡単な印刷され、終了時に何かをするあなたの機能であるスタックトレースは

onexit(){ while caller $((n++)); do :; done; }

代わりにERRをトラップする同様のオプション-E/errtraceがあります。例:

trap onerr ERR

ゼロステータスの例:

$ true; echo $?
0

ゼロ以外のステータスの例:

$ false; echo $?
1

否定状態の例:

$ ! false; echo $?
0
$ false || true; echo $?
0

pipefail無効にしてテストする:

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

pipefail有効にしてテストする:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

54

のために中止されたスクリプトの終了ステータスを把握しようとしたときに、この投稿を見つけましたset -e。その答えは私には明らかではなかった。したがって、この答え。基本的に、set -eコマンド(シェルスクリプトなど)の実行を中止し、失敗したコマンド(つまり、外部スクリプトではなく内部スクリプト)の終了ステータスコードを返します

たとえば、次のシェルスクリプトがあるとしますouter-test.sh

#!/bin/sh
set -e
./inner-test.sh
exit 62;

のコードinner-test.shは次のとおりです。

#!/bin/sh
exit 26;

outer-script.shコマンドラインから実行すると、外側のスクリプトが内側のスクリプトの終了コードで終了します。

$ ./outer-test.sh
$ echo $?
26

10

問題のスクリプトがすぐに失敗することを意図していると思います。

これを自分でテストするには、単にset -ebashプロンプトで入力します。さあ、実行してみてくださいls。ディレクトリ一覧が表示されます。次に、と入力しlsdます。そのコマンドは認識されず、エラーコードが返されるため、bashプロンプトは閉じます(によりset -e)。

これを「スクリプト」のコンテキストで理解するには、次の簡単なスクリプトを使用します。

#!/bin/bash 
# set -e

lsd 

ls

そのまま実行するとls、最終行のからディレクトリリストが表示されます。のコメントを外してset -e再度実行すると、bashがからのエラーに遭遇すると処理が停止するため、ディレクトリリストは表示されませんlsd


この回答は、質問で他の人にまだ与えられていない洞察や情報を追加しますか?
Charles Duffy

6
他の回答にはない機能の明確で簡潔な説明を提供していると思います。他には何もありません。他の応答よりも焦点が絞られています。
Kallin Nagelberg、

9

これは古い質問ですが、Debianパッケージ処理スクリプトでのset -e別名の使用については、ここでの回答のいずれも説明していませんset -o errexit。これらのスクリプトでは、Debianポリシーに従って、このオプションの使用が必須です。意図は明らかに、未処理のエラー状態の可能性を回避することです。

これが実際に意味することは、実行するコマンドがエラーを返す可能性がある状況で理解し、それらの各エラーを明示的に処理する必要があるということです。

一般的な落とし穴は、たとえばdiff(違いgrepがある場合はエラーを返す)と(一致がない場合はエラーを返す)です。明示的な処理でエラーを回避できます。

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(現在のスクリプト名をメッセージに含めるように注意し、診断メッセージを標準出力ではなく標準エラーに書き込む方法にも注意してください。)

明示的な処理が本当に必要または役に立たない場合は、明示的に何もしません。

diff this that || true
grep cat food || :

(シェルの:no-opコマンドの使用は少しあいまいですが、かなり一般的に見られます。)

繰り返しますが

something || other

の略記です

if something; then
    : nothing
else
    other
fi

つまりothersomething失敗した場合にのみ実行するように明示的に言います。ロングハンドif(および他のシェルフロー制御ステートメントなど)whileuntil)もエラーを処理するための有効な方法です(確かに、それがなかったら、とシェルスクリプトset -eのフロー制御文を含めることができませんでした!)

また、明示的に言うと、このようなハンドラーがない場合、次の場合にset -eスクリプト全体がすぐにエラーで失敗します。diffgrepがない場合、相違点が見つかった場合、または一致が見つからなかった場合、。

一方、コマンドによっては、必要なときにエラー終了ステータスを生成しないものがあります。一般的に問題のあるコマンドはfind(exitステータスはファイルが実際に見つかったかどうかを反映しない)およびsed(exitステータスは、スクリプトが入力を受け取ったか、実際にコマンドを正常に実行したかを明らかにしません)。いくつかのシナリオでの単純なガードは、出力がない場合に悲鳴を上げるコマンドにパイプすることです。

find things | grep .
sed -e 's/o/me/' stuff | grep ^

パイプラインの終了ステータスは、そのパイプラインの最後のコマンドの終了ステータスであることに注意してください。したがって、上記のコマンドは、実際には完全の状態をマスクfindし、sedgrep最終的に成功したかどうかのみを通知します。

(もちろん、Bashにはがありset -o pipefailますが、DebianパッケージスクリプトはBash機能を使用できません。ポリシーshはこれらのスクリプトにPOSIX を使用することを固く定めていますが、常にそうであるとは限りませんでした。)

多くの状況で、これは防御的にコーディングするときに個別に注意する必要があるものです。場合によっては、たとえば一時ファイルを調べて、その出力を生成したコマンドが正常に終了したかどうかを確認する必要があります。イディオムと便利さがシェルパイプラインを使用するように指示されている場合でも同様です。


これは素晴らしい答えです。そしてそれはベストプラクティスを促進します。GREPコマンドでまったく同じ問題が発生し、「set -e」を削除したくありませんでした
Minnie

7
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.