大多数 Linux 指南由“你需要运行command_1
,然后command_2
,然后command_3
”等页面组成。由于我不想浪费时间手动运行所有这些,我宁愿创建一个脚本
command_1
command_2
command_3
Run Code Online (Sandbox Code Playgroud)
并运行一次。但是,通常情况下,某些命令会失败,而我不知道哪些命令失败了。此外,如果早些时候出现故障,通常所有其余命令都没有意义。所以一个更好的脚本应该是这样的
command_1
command_2
command_3
Run Code Online (Sandbox Code Playgroud)
但是它需要编写太多的样板代码,每个命令重复 3 次,而且很有可能我错误地输入了一些大括号。有没有更方便的方法来做最后一个脚本的作用?特别是:
有两种类型的解决方案:
try
语句并将链接的命令拆分为单独的命令(Jasen 的回答) .你们这些摇滚乐手,但我会暂时搁置这个问题。也许有人知道满足这两种需求的解决方案(在最后一行打印失败的命令并允许复制粘贴命令而不进行修改)。
use*_*001 62
一种选择是将命令放在 bash 脚本中,并以 .bashrc 开头set -e
。
如果任何命令以非零退出状态退出,这将导致脚本提前终止。
另请参阅有关堆栈溢出的此问题:https : //stackoverflow.com/q/19622198/828193
要打印错误,您可以使用
trap 'do_something' ERR
Run Code Online (Sandbox Code Playgroud)
do_something
您将创建的命令在哪里显示错误。
这是一个脚本示例,用于查看其工作原理:
#!/bin/bash
set -e
trap 'echo "******* FAILED *******" 1>&2' ERR
echo 'Command that succeeds' # this command works
ls non_existent_file # this should fail
echo 'Unreachable command' # and this is never called
# due to set -e
Run Code Online (Sandbox Code Playgroud)
这是输出:
$ ./test.sh
Command that succeeds
ls: cannot access 'non_existent_file': No such file or directory
******* FAILED *******
Run Code Online (Sandbox Code Playgroud)
此外,正如@jick所提到的,请记住,管道的退出状态默认是其中最终命令的退出状态。这意味着如果管道中的非最终命令失败,则不会被set -e
. 如果您担心这个问题,要解决这个问题,您可以使用set -o pipefail
正如我的@glenn jackman和@Monty Harder所建议的那样,使用函数作为处理程序可以使脚本更具可读性,因为它避免了嵌套引用。由于我们现在正在使用一个函数,我set -e
完全删除了,并exit 1
在处理程序中使用,这也可以使其对某些人更具可读性:
#!/bin/bash
error_handler() {
echo "******* FAILED *******" 1>&2
exit 1
}
trap error_handler ERR
echo 'Command that succeeds' # this command works
ls non_existent_file # this should fail
echo 'Unreachable command' # and this is never called
# due to the exit in the handler
Run Code Online (Sandbox Code Playgroud)
输出与上面相同,但脚本的退出状态不同。
小智 20
想要一个不寻常的解决方案?如果已make
安装,则可以将命令列表放在Makefile
for 中make
以执行。附加优势:您不必检查是否发生了任何错误。make
将自动检查每个命令的返回值。如果它不为零,则配方因错误而终止。如果您想忽略某些命令的错误,请使用|| true
.
示例生成文件:
.PHONY: all
.SILENT:
all:
echo "Started list of commands."
true
echo "Executing a command which will fail, but I want to ignore failure."
false || true
echo "Executing a command which will definitely fail."
false
echo "This code will not be reached."
Run Code Online (Sandbox Code Playgroud)
注意:确保 (heh) 像上面一样缩进你的命令,但要使用制表符。
小智 16
我相信这仅适用于 bash,但您可以尝试:
set -o xtrace
set -o errexit
Run Code Online (Sandbox Code Playgroud)
或者,如果你想简洁,
set -ex
Run Code Online (Sandbox Code Playgroud)
这将做两件事:errexit
(-e) 将在出现错误时中止脚本,以及xtrace
(-x) 将在 bash 执行之前打印每个命令,因此在失败的情况下,您确切地知道它正在执行什么。
一个缺点是它会使输出混乱,但只要您同意,这是一个非常好的解决方案,只需最少的工作。
set -o pipefail
:是个好主意:否则,如果您运行foo | bar
,则失败foo
将被静默忽略。(警告:它巧妙地更改了管道语句的“退出代码”,因此如果您正在修改其他人的脚本,请谨慎使用。此外,有时您实际上并不关心foo
的失败,因此显然您不能使用pipefail
在这种情况下。)你可以这样做:
$ for f in command_1 command_2 command_3 command_4
do
$f
rc=$?
if [[ 0 != $rc ]]; then echo Failed command: ${f}; break; fi
done
Run Code Online (Sandbox Code Playgroud)
这假设命令没有选项,如果有,则需要将每个命令/选项集括在引号内。