$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
それは fortune
条件なしのプログラム exit(rand())
.
説明できますか? ここで何が間違っているのか?
叙情的歴史的余談
私がこのハイゼンバグを初めて知ったのは今から四半世紀前です。 次に、FaxNET のゲートウェイ用に、次の方法でいくつかのユーティリティを作成する必要がありました。
以前に sendmail と uucp/uupc のバグに対処した経験により、「徹底したエラー処理」に対する私の熱心さがさらに高まりました。 その話の詳細を掘り下げる意味はありませんが、私はこのハイゼンバグと10週間、14〜XNUMX時間格闘しました。 それで、それを思い出して、昨日、その旧知の人がまた立ち寄ってくれました。
TL;DR 回答
ユーティリティ head
缶 ~からのチャンネルを閉じる fortune
一度に 彼が最初の行を読むとすぐに。 もし fortune
複数行を出力し、その後、対応する呼び出しを出力します。 write()
エラーを返すか、要求されたバイト数よりも少ないバイト数が出力されたことを報告します。 また、エラー処理を慎重に行って書かれています fortune
は、この状況を終了ステータスに反映する権利を有します。 その後、インストールのため set -o pipefail
働くでしょう || echo "Вы проиграли"
.
しかし、 head
間に合わないかもしれない 前に閉じる fortune
データの出力を終了します。 そうすればうまくいきます && echo "Повезло!"
.
今日の私の中で GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
人間に翻訳
ここではよくあることですが、 --version
彼が誰であるかを尋ねられ、オプションがサポートされていない場合は、スタブが挿入されます。 「GCC または CLANG 互換のコンパイラを使用してください」.
以下のような
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
率直に言って、私は昔の「知人」をすぐには認識できませんでした。 さらに、このプロジェクトはすでに Elbrus 上で、また Alt を含むさまざまなディストリビューションで何度もテストされています。 さまざまなコンパイラ、GNU Make および bash のバージョンを使用します。 したがって、私はここで自分の間違いを見たくありませんでした。
問題を再現しようとしたり、何が起こっているのかを理解しようとしたりすると、さらに奇妙なことが起こり始めました。
コマンドラインの呪文:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
時々、余分なテキストが生成されたり、生成されなかったりします... 多くの場合、オプションの XNUMX つがかなり長い間表示されますが、より長く突き続けると、常に両方が表示されます。
もちろん、 strace
ちなみに、自尊心のある人たちと同じように、 strace
再現しないことを好みます。
どうしたの?
- ユーティリティ
head
は、要求された行数を読み取るとすぐに、読み取られているチャネルを閉じる権利を持っています (または、強制さえされています)。 - データを生成するプログラム作成者 (この場合は
cc
) 缶 複数行を印刷し、 無料 複数の呼び出しを通じてこれを行うwrite()
. - もし ライター側での記録が終了する前にリーダー側でチャンネルを閉じる時間があり、ライター側でエラーが発生します。
- ライタープログラム 権利がある どちらもチャネル書き込みエラーを無視し、完了コードに反映します。
- 設置の都合上
set -o pipefail
少なくとも XNUMX つの要素の結果がゼロ以外の場合、パイプライン完了コードはゼロ以外 (エラー) になり、その後は機能します。|| echo "Please use GCC or CLANG compatible compiler"
.
ライター プログラムが信号をどのように処理するかによって異なる場合があります。 たとえば、プログラムが異常終了する (ゼロ以外の/エラー終了ステータスが自動的に生成される)、または write()
要求および設定されたバイト数よりも少ないバイト数を書き込んだ結果を返します。 errno = EPIPE
.
誰が責任があるのですか?
説明されているケースでは すべてのことを少しずつ。 でのエラー処理 cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) ではない 冗長です。 多くの場合、慎重に誤ったほうが良いですが、これにより、従来の動作用に設計された定型文の突然の欠陥が明らかになります。
何をしますか?
違う:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
正しい:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
ここで、パイプの早期終了は、ネストされたパイプライン (括弧「内」) を処理するときにコマンド インタープリターによって処理されます。 したがって、もし
fortune
ステータスで閉じられたチャネルへの書き込みエラーを報告し、出力|| echo "Ошибка"
チャネルはすでに閉じられているため、どこにも到達できません。 -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
ユーティリティはこちら
cat
エラーを無視するため、ダンパーとして機能します。EPIPE
撤退時。 結論としてはこれで十分ですfortune
小さく(数行)、チャネル バッファーに収まります(512 バイトから約 64K、ほとんどの OS ≧ 4K)。 そうしないと、問題が再発する可能性があります。
正しい処理方法 EPIPE
その他の録音エラーはありますか?
唯一の正しい解決策はありませんが、簡単な推奨事項があります。
EPIPE
必須 処理する必要があります 整合性が必要なデータを出力するとき (および終了ステータスに反映されます)。 たとえば、アーカイバやバックアップ ユーティリティの操作中などです。EPIPE
無視したほうがいい 情報や補助メッセージを表示するとき。 例えばオプション情報を表示する場合--help
または--version
.- 開発中のコードが事前にパイプラインで使用できるかどうか
| head
その後EPIPE
無視する方が良いですが、そうでない場合は処理して終了ステータスに反映する方が良いです。
この場を借りてチームの皆様に感謝の意を表したいと思います
それを続けてください、仲間たち、続けてください
感謝
KDPVから
出所: habr.com