このスクリプトが最新のファイルを確実にログに記録しないのはなぜですか?

このスクリプトが最新のファイルを確実にログに記録しないのはなぜですか?

このスクリプトは時々失敗し、時々成功します (非常に厄介な状況です)。

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{001..312}
latest_file="$(find $test_dir -type f | sort -r | head -n1)"
echo $latest_file

成功すると、次のようなログが記録されます。

++ mktemp -d
+ test_dir=/tmp/tmp.yWelcpBYB7
+ touch /tmp/tmp.yWelcpBYB7/file001 /tmp/tmp.yWelcpBYB7/file002 ... /tmp/tmp.yWelcpBYB7/file312
++ find /tmp/tmp.yWelcpBYB7 -type f
++ head -n1
++ sort -r
+ latest_file=/tmp/tmp.yWelcpBYB7/file312
+ echo /tmp/tmp.yWelcpBYB7/file312
/tmp/tmp.yWelcpBYB7/file312

失敗すると、次のようなログが記録されます。

++ mktemp -d
+ test_dir=/tmp/tmp.VzTqmgpZyG
+ touch /tmp/tmp.VzTqmgpZyG/file001 /tmp/tmp.VzTqmgpZyG/file002 ... /tmp/tmp.VzTqmgpZyG/file312
++ find /tmp/tmp.VzTqmgpZyG -type f
++ sort -r
++ head -n1
+ latest_file=/tmp/tmp.VzTqmgpZyG/file312

このecho $latest_file線はないxtraceに表示されているにもかかわらずここで実行される

10,000ファイルを使用すると正常に実行できないので、何か関係があるのではないかと思います。停止する探す早い。

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{0000..9999}
latest_file="$(find $test_dir -type f | sort -r | head -n1)"
echo $latest_file

エラー時の停止を抑制すると(+e を設定)、成功します:

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{0000..9999}
set +e
latest_file="$(find $test_dir -type f | sort -r | head -n1)"
set -e
echo $latest_file

このスクリプトが最新のファイルを確実にログに記録しないのはなぜですか?

答え1

問題は-e. Why?です-e。プロセスがゼロ以外の終了コードで終了した場合に bash が中止します (完全なルールはもう少し複雑です)。パイプがある場合は、最後のコマンドだけがカウントされます。

は、からの残りの出力を無視するためにhead -n1パイプを壊す必要があるため ( で確認できます)、内部的にエラー状況が発生します。stracesort

したがって、スクリプトが で確実に動作するようにするには、パイプの末尾に-eを追加します。は依然としてパイプを中断しますが、パイプ内の最後のコマンドではなくなるため、 では考慮されません。はパイプに対して何も行いません。cathead-ecat

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{0000..9999}
latest_file="$(find $test_dir -type f | sort -r | head -n1 | cat)"
echo $latest_file

チェックしてくださいset -e (または set -o errexit、または trap ERR) が期待どおりに動作しないのはなぜですか?なぜこのような不安定な機能なのか、またそれがどのような問題を引き起こす可能性があるのか​​を知る必要があります-e。最後に多くの例があります。私のお気に入りは:

#!/bin/bash

set -e
foo=$(expr 1 - 1)
echo survived

それはない印刷生き残った、その行は実行されません。しかし、 があった場合foo=$(expr 2 - 1)、 がecho実行されます。

独自のエラー チェックを実装する方がよいでしょうが、-eこれは最善の解決策ではありません。

関連情報