我有一个bash脚本,是作为bash作业提交的。它创建一些文件,执行一些计算,将输出文件移到其他地方并清理。为了移动输出文件,它包含以下几行:
set -e
mv $tmp/stdout.txt $current/tmp.stdout.txt
grep Report $current/tmp.stdout.txt >/dev/null 2>&1
mv $current/tmp.stdout.txt $current/stdout.txt
set +e
如果计算成功,则输出文件stdout.txt
包含几行以Report
开头的行;但是没有的话就没有。进一步的处理将检查$current/stdout.txt
文件是否存在(否则重新提交作业)。
第一个mv
使用临时名称将输出文件从临时目录移动到最终目录;第二个mv
将输出文件重命名为其最终名称。但是,grep
之间的目的是什么?如果输出文件包含带有Report
的行,它们将被重定向到\dev\null
,并且什么也没有发生。如果输出文件不包含带有Report
的行,则它不会输出任何内容,既不会输出到重定向的stdout
,也不会输出到重定向的stderr
。因此,我的印象是该行不执行任何操作,因此我应将mv
+ grep
+ mv
替换为一个mv
。我在这里忽略了哪些功能?
答案 0 :(得分:6)
set -e
在这里很重要。
grep
将其退出状态设置为0,表示已成功处理输入文件并找到了任何结果,否则返回非零值。set -e
告诉外壳程序退出,如果任何已检查的命令具有非零退出状态。 (它有很多陷阱和警告,通常不应该使用;请参阅BashFAQ #105。)因此-除非将此代码嵌入触发set -e
无效的几种情况之一的上下文中-如果mv
,脚本将在第二个grep
之前终止没有匹配项。
编写脚本这一部分的更好方法是:
mv "$tmp/stdout.txt" "$current/tmp.stdout.txt" || exit
grep -q Report "$current/tmp.stdout.txt" || exit
mv "$current/tmp.stdout.txt" "$current/stdout.txt" || exit
grep -q
比grep >/dev/null
更有效率,因为它可以在看到匹配项时立即退出,否则grep
需要一直读取到输入文件的末尾。 (2>/dev/null
通常是不好的做法,因为它隐藏了您需要了解的有关调试不当行为的错误;因此,请在此处将其删除。)|| exit
放在要致命的错误的单个命令上比依赖set -e
可靠得多(请跳过以下练习的寓言,如果急忙,或查看https://www.in-ulm.de/~mascheck/various/set-e/以了解已知set -e
的行为在不同的shell和/或shell版本之间有所不同的情况的列表。答案 1 :(得分:2)
如果未找到匹配项,Grep将返回错误代码。
set -e
表示错误将停止脚本。
grep上还有其他选项,这意味着它没有输出,而不是进行所有捕获。
答案 2 :(得分:1)
set -e
将bash
配置为在遇到第一个错误时中止。如果grep
失败(不查找任何内容),则bash
将在grep
之后终止。
大多数grep
版本都知道-q
选项,该选项使它们安静(抑制所有输出),因此不再需要重定向。另外,依赖set -e
的代码也不容易维护。适当的grep ... || exit 1
会更明确。