【问题标题】:"mkdir || echo && exit" exiting even when mkdir succeeds即使 mkdir 成功,“mkdir || echo && exit”也会退出
【发布时间】:2017-11-11 18:02:20
【问题描述】:

mkdir $2 || echo "I can't create directory $2" && exit 8

大家好,这是我在这里的第一篇文章,请善待。

我现在正在制作一个脚本,这行让我很困扰。

只有在无法创建目录 $2 时才会出现出口 8。 运行脚本并成功创建该目录后,它仍然在 8 退出。

我错过了什么吗?我认为“ || ”之后的命令只有在左侧出现错误时才会发生。

我是 Linux 世界的新手,作为一个有一点到中等 C 经验的人,我很困惑,求助! (使用 ubuntu,bash,顺便说一句)

【问题讨论】:

  • 我认为是因为运算符的优先级。你可以放括号。如果 mkdir 失败,则执行 echo 并返回 true,因此执行第三个命令。也许你可以使用括号
  • 他是来自 Unix&Linux SE 的回答:unix.stackexchange.com/questions/88850/…

标签: linux bash shell ubuntu


【解决方案1】:

正如@fernand0 所建议的,问题在于||&& 运算符的优先级。您希望它运行类似于mkdir || ( echo && exit ) 的东西——也就是说,如果mkdir 失败,则运行echo && exit 部分。但它实际上正在运行类似( mkdir || echo ) && exit 的东西——也就是说,如果mkdirecho 命令成功,它将运行exit 部分。 echo 几乎总是会成功,所以它几乎总是会退出。

因此,您需要明确地对命令进行分组,以覆盖此优先级。但是不要使用( ),因为它会在子shell 中运行它的内容,而exit 只会退出子shell,而不是主脚本;您可以改用{ },或使用显式的if 块。此外,您实际上并不需要echo && exit,因为只有在echo 命令成功时才会运行exitecho 几乎总是成功,但在极少数情况下失败,我很确定您希望脚本无论如何都退出。

当我需要在脚本中做这样的事情时,我通常使用这个成语:

mkdir "$2" || {
    echo "I can't create directory $2" >&2
    exit 8
}

(注意:正如@CharlesDuffy 建议的那样,我在$2 周围添加了双引号——双引号变量引用几乎总是一个好主意,以防它们包含任何空格、通配符等。我还发送了错误消息到标准错误 (>&2) 而不是标准输出,这通常也是一种更好的处理方式。)

如果你想更简洁,可以放在一行:

mkdir "$2" || { echo "I can't create directory $2" >&2; exit 8; }

请注意,} 之前需要最后的;(或换行符),否则shell 认为} 只是exit 的一个参数。您也可以采用另一种方式并使用明确的if 块:

if ! mkdir "$2"; then
    echo "I can't create directory $2" >&2
    exit 8
fi

这个选项不太聪明和简洁,但这是一件好事——聪明和简洁正是导致这个问题的首要原因;清晰明确对代码来说更好。

【讨论】:

  • @CharlesDuffy 完成!
  • 你知道我有多爱你吗?另外,你能帮我解决这个问题吗: > var=$(file -b $1 | grep "ISO") 我在脚本中有那行。如果文件失败(例如:当没有参数时),我会收到一条关于其使用的巨大错误消息。如何使用 2> 将该错误消息发送到 /dev/null ?我尝试了几个选项,但都没有奏效。
  • @NuSuntStudent 你需要重定向打印错误命令的stderr,所以var=$(file -b "$1" 2>/dev/null | grep "ISO")。请注意,我还在$1 周围添加了双引号,这会将错误消息更改为关于找不到没有名称的文件的错误消息。我建议在脚本的开头添加对脚本参数的完整性检查(例如if [ -z "$1" ]; then echo "Usage: ..." >&2; exit 1; fi)。这允许您给出描述性错误,而不是用户必须从脚本的哪些部分失败中找出问题所在。
  • “聪明而简洁正是导致这个问题的首要原因”。说得好。
【解决方案2】:

||&& 在其他语言中没有您习惯的优先级。不加括号,它相当于(a || b) && c(严格从左到右),而不是a || (b && c)(其中&& 的优先级高于||)。将命令与||&& 混合在一起并不是一个好主意;相反,请使用 if 语句。

if ! mkdir "$2"; then
  echo "I can't create directory $2" >&2
  exit 8
fi

如果您真的想使用列表运算符,请使用{ ... } 对命令进行适当的分组。

mkdir "$2" || { echo "I can't create directory $2" >&2; exit 8; }

【讨论】:

  • echo && exit 是错误的形式——如果echo 失败,我们真的要避免退出吗?那应该是mkdir "$2" || { echo "..." >&2; exit 8; }
  • 同意,不过如果 echo 这么简单失败,你可能有更大的问题需要解决。