為什麼這個腳本不能可靠地記錄最新文件?

為什麼這個腳本不能可靠地記錄最新文件?

該腳本有時會失敗,有時會成功(這是一種非常煩人的情況):

#!/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.為什麼?-e如果進程以非零退出程式碼退出,則 bash 中止(完整規則有點複雜)。如果有管道,則僅計算最後一個命令。

head -n1在內部創建了一個錯誤情況,因為它必須破壞管道(您可以使用 檢查它strace)以忽略 的其餘輸出sort

因此,為了使您的腳本可靠地使用-e,您可以cat在管道末尾添加 a 。head仍然會破壞管道,但由於它不再是其中的最後一個命令,因此 不會考慮它-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這不是最好的解決方案。

相關內容