重击忽略特定命令的错误


444

我正在使用以下选项

set -o pipefail
set -e

在bash脚本中停止执行错误。我有大约100行脚本正在执行,并且我不想检查脚本中每一行的返回码。

但是对于一个特定的命令,我想忽略该错误。我怎样才能做到这一点?

Answers:


756

解决方案:

particular_script || true

例:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three 将永远不会打印。

另外,我想补充一点,当pipefail打开时,如果管道中的命令之一具有非零退出代码(pipefail关闭该命令时必须关闭最后一个),那么shell认为整个管道具有非零退出代码就足够了。

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0

15
+1。正如Bash参考手册所解释的那样,当-e属性设置为“如果失败的命令是紧跟在a whileuntil关键字之后的命令列表的一部分,if语句中的测试的一部分,所执行的任何命令的一部分时,“不退出外壳程序” 在一个&&||名单除了在最终的命令&&||管道中,任何命令,但最后,或者命令的返回状态被倒置!“。
ruakh 2012年

@IgorChubin我不知道为什么,但是这不起作用output = (ldd $2/bin/* || true) | grep "not found" | wc -l当ldd返回失败时脚本在此行之后终止
Vivek Goel 2012年

1
(ldd $2/bin/* || true) | grep "not found" | wc -l || true
伊戈尔·楚宾

1
因为set -o pipefail。当grep什么都没找到时,它返回非零退出代码,shell认为整个管道具有非零退出代码就足够了。
伊戈尔·楚宾

3
如果要保留返回值(不退出)的选项,请尝试使用mycommand && true。这使您可以在后续步骤中检查返回代码并以编程方式进行处理。
Ed Ost,

178

只需|| true在命令后添加要忽略错误的位置即可。


11
我认为添加此代码很重要:此方法将允许响应代码和错误消息持续存在,而“!” 下面概述的方法将更改响应代码,因此不会产生错误。这在使用set -e和尝试捕获错误消息时很重要:例如set -e; TEST=$(foo 2>&1 || true); echo $TEST
Spanky

87

不要停止并保存退出状态

以防万一您希望脚本在特定命令失败时不停止,并且还希望保存失败命令的错误代码:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE

2
这正是我需要的
sarink

由于某种原因对我不起作用...哦,好
Jeef,

当命令返回0时,EXIT_CODE未设置为零。但是,将捕获非零退出代码。知道为什么吗?
Ankita13

@ Ankita13因为如果为零,则第一个command成功,因此需要运行之后的内容||。读取为:如果command失败,请执行EXIT_CODE=$?。您可能只是做command || echo "$?"或使用“陷阱”进行更详细的调试。看到这个-> stackoverflow.com/a/6110446/10737630
tinnick

63

更简洁地说:

! particular_script

POSIX规范set -e(重点是):

启用此选项时,如果简单命令由于Shell错误的后果中列出的任何原因而失败,或者返回退出状态值> 0,并且在经过一会儿,直到或if关键字之后,则不属于复合列表的一部分,并且不是AND或OR列表的一部分,也不是前面带有的管道保留字,然后外壳应立即退出。


16
这仅会反转命令的退出代码,因此成功完成的命令将返回1而不是0,并使用失败脚本-e
Marboni

17
我的理解是,!无论如何,它将防止外壳退出。该脚本Still alive!在我运行时显示该消息,表明该脚本已运行完毕。您看到不同的行为了吗?
Lily Finley 2015年

7
没错,它会反转退出状态,但是当命令以0或1结尾时,它不会使脚本崩溃。无论如何,感谢您引用文档,我将表达式放在了if子句中并解决了问题。
Marboni

42

除了“返回true”,还可以使用“ noop”或null实用程序(如POSIX规范中所述),:而只是“不执行任何操作”。您将保存一些字母。:)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."

3
尽管||:不太像|| 真(这可能会混淆是非专业用户),我喜欢它简洁
大卫·

此变体不返回在CI中可能很重要的任何文本。
kivagant

5

如果要防止脚本失败收集返回代码,请执行以下操作:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

returncode 无论命令是成功还是失败,都将收集命令。


2

在使用CLI工具时,我一直在使用下面的代码段,我想知道是否存在某些资源,但是我不在乎输出。

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi

1
这似乎是一种回旋处且价格适中的说法if [ -r not_exist ]
Tripleee '18

1

我有点喜欢这种解决方案:

: `particular_script`

执行反勾号之间的命令/脚本,并将其输出馈送到命令“:”(等效于“ true”)

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

编辑:修复了难看的错字


0

虽然|| true是首选,但您也可以

var=$(echo $(exit 1)) # it shouldn't fail

0

从这里没有解决方案对我有用,所以我找到了另一个解决方案:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

这对于CI和CD很有用。这样,将打印错误消息,但整个脚本将继续执行。


0
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

使用此示例创建日志文件的示例

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi

0

感谢您上面提供的简单解决方案:

<particular_script/command> || true

以下构造可用于脚本步骤的其他操作/故障排除和其他流控制选项:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

exit 1如果需要,我们可以阻止进一步的行动。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.