如果命令成功或失败,如何有条件地做某事

437 bash control-flow

我怎样才能在 bash 中做这样的事情?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi
Run Code Online (Sandbox Code Playgroud)

Kei*_*son 535

如果命令成功或失败,如何有条件地做某事

这正是 bash 的if声明所做的:

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi
Run Code Online (Sandbox Code Playgroud)

从留言中加入信息:您没有需要使用[...]语法,在这种情况下。[本身就是一个命令,非常接近于test. 它可能是在 an 中最常用的命令if,这可能会导致假设它是 shell 语法的一部分。但是如果你想测试一个命令是否成功,直接使用命令本身和if,如上所示。

  • @Joe:我想你的意思是`如果!命令 ; 然后 ... ; 菲`。`[` 本身就是一个命令,在这种情况下不需要它。 (44认同)
  • 或者您可以将 `then` 放在单独的行上。 (34认同)
  • @Joe:我的方式也有正确的优点。`如果[!command ]` 不执行 `command`;它将 `command` 视为字符串并将其视为真,因为它具有非零长度。`[` 是 [`test`](http://linuxmanpages.com/man1/test.1.php) 命令的同义词 (26认同)
  • 请注意,分号很重要。 (20认同)
  • 哎呀。你是对的。 (7认同)
  • 小心,这假定 `command` 返回一个合理的返回值。今天大多数人都这样做,但是例如`vi(1)`(最初的那个)因其随机返回值而(臭名昭著)。 (3认同)
  • @cgseller 如果你想在 `if` 和 `then` 之间使用多个命令(比如管道 `|` 或列表 `;`、`&`、`&&`、`||`),你只需像这样把它们放在那里: `如果ssh 无效| 记录器;然后回声“嗨”;fi` --- 如果你真的想再次包含命令列表,你可以使用大括号 `{}` 或圆括号 `()`。--- 构造 `$(ssh invalid | logger)` 被命令的 stdout 输出(在这种情况下是 `logger` 的输出)替换,并且如果构造位于命令的位置(如在您的示例中)然后替换的输出作为命令执行。 (3认同)
  • @DavidParks:“command”只是任意命令,就像您在 shell 提示符下单独键入它一样。不,它不需要用反引号或“$(...)”;它将被命令的输出替换(除非您想执行由另一个命令的输出给出的命令)。例如:`if cmp -s file1 file2 ; 然后回显相同;否则回显不同;菲` (3认同)
  • @vonbrand:当然,但我认为检查“vi”的返回值并不常见。 (2认同)
  • @KeithThompson,你会感到惊讶......;-)(通过实验发现了这个错误特征) (2认同)
  • 如何格式化“命令”?一个简单的例子会对这个答案有很大帮助。‘command’ 需要用反引号吗?子进程“$(command)”?报价单/双?ETC? (2认同)
  • @Freedo 来自 bash 文档:“管道的退出状态是管道中最后一个命令的退出状态,除非启用了“pipefail”选项(*注意设置内置::)。如果启用了“pipefail” , 管道的返回状态是最后一个(最右边)以非零状态退出的命令的值,如果所有命令都成功退出,则返回零。如果保留字 '!' 在管道之前,退出状态是上述退出状态的逻辑非。shell 在返回值之前等待管道中的所有命令终止。 (2认同)

Bru*_*ger 197

对于您希望在 shell 命令有效时发生的小事情,您可以使用以下&&构造:

rm -rf somedir && trace_output "Removed the directory"
Run Code Online (Sandbox Code Playgroud)

同样,对于您希望在 shell 命令失败时发生的小事情,您可以使用||

rm -rf somedir || exit_on_error "Failed to remove the directory"
Run Code Online (Sandbox Code Playgroud)

或两者

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"
Run Code Online (Sandbox Code Playgroud)

对这些结构做太多事情可能是不明智的,但它们有时可以使控制流更加清晰。

  • 它们更短并且(至少在某些 shell 中)更快。我不寒而栗地想起一个怪物 Ultrix 安装脚本,它是用我曾经试图破译的这些条件结构编写的...... (4认同)
  • 应该注意的是,最后一个示例中“&&”和“||”的顺序很重要。如果您颠倒顺序,如`rm ... || command1 && command2`,如果 `rm` 失败,您最终将执行这两个命令,因为这里的 `&&` 适用于 `command1` 并假设它也没有失败,这将导致 `command2` 运行。以相反的顺序 `rm ... && command2 || command1` 的双重执行不是问题,假设 `command2` 永远不会失败。 (4认同)
  • 还值得注意的是,如果您需要多个命令,您可以在子 shell 中链接项目。这几乎总是会导致意大利面条代码的情况,并且应该使用常规的“if”语句。但如果需要,您可以运行: `command && (command1; command 2) || (命令 3;命令 4)` (2认同)

小智 167

检查 的值$?,其中包含执行最近的命令/函数的结果:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi
Run Code Online (Sandbox Code Playgroud)

  • 这个习惯用法有好处——它保留了返回值。总而言之,我发现这个功能更强大,但更冗长。它也更容易阅读。 (28认同)
  • 虽然技术上是正确的(因此不保证不赞成),但它没有使用 Bash 的“if”习语。我更喜欢基思汤普森的回答。 (13认同)
  • 我更喜欢这样的可读性。主要命令通常很长。将它放在“if”语句中会降低可读性。 (7认同)
  • 什么是“Bash 的 if 成语”? (5认同)
  • @Noaker 事实上,`if` 的唯一目的就是要做到这一点。Bash 中的流控条件都在后台检查`$?`;这就是他们*所做的。*在绝大多数情况下,明确检查其值应该是不必要的,并且通常是初学者的反模式。 (5认同)

小智 70

这对我有用:

command && echo "OK" || echo "NOK"
Run Code Online (Sandbox Code Playgroud)

如果command成功,则echo "OK"执行,因为它成功,执行在那里停止。否则,&&跳过并echo "NOK"执行。

  • 此外,如果 `echo "OK"` 部分本身可能会失败,那么最好这样做: `command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))` (11认同)
  • 如果你想在失败时做一些事情,并保留退出代码(在命令提示符中显示或在脚本中测试),你可以这样做:`command && echo "OK" || c=$?; 回声“NOK”;$(退出 $c)` (5认同)
  • @Sam-Hasler:不应该是`command && echo "OK" || (c=$?; echo "NOK"; (exit $c))`? (3认同)

Ser*_*nyy 9

应该注意的是,if...then...fi&&/||类型的方法处理我们要测试的命令返回的退出状态(成功时为 0);但是,如果命令失败或无法处理输入,某些命令不会返回非零退出状态。这意味着通常的if&&/||方法不适用于这些特定命令。

例如,在 Linux 上file,如果GNU接收到一个不存在的文件作为参数并且find找不到用户指定的文件,它仍然会以 0 退出。

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们可以处理这种情况的一种潜在方法是读取stderr/stdin消息,例如那些由file命令返回的消息,或者像 in 解析命令的输出find。为此,case可以使用语句。

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found
Run Code Online (Sandbox Code Playgroud)