为什么该脚本不能可靠地记录最新文件?

为什么该脚本不能可靠地记录最新文件?

这个脚本有时候会失败,有时候会成功(非常烦人的情况):

#!/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在内部创建了一个错误情况,因为它必须破坏管道(您可以使用来检查它strace)以忽略其余的输出sort

因此,为了使您的脚本可靠地与 一起工作-e,您可以cat在管道末尾添加一个。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这不是最好的解决方案。

相关内容