为什么在 Bash 脚本中将命令组合在一行上?

Ala*_*inD 38 command-line bash scripts

我是 Linux 和 Bash 脚本的新手。在工作中,我看到 Bash 脚本的结构与此类似:

mkdir build && cd build && touch blank.txt
Run Code Online (Sandbox Code Playgroud)

或者:

mkdir build; cd build; touch blank.txt
Run Code Online (Sandbox Code Playgroud)

甚至是异国情调:

COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}
Run Code Online (Sandbox Code Playgroud)

最后一个示例给出了一个可能的用例,其中一行可能有用,但通常以下内容更易于阅读并且(至少对我而言)允许您直观地调试脚本:

mkdir build
cd build
touch blank.txt
Run Code Online (Sandbox Code Playgroud)

在一条线上塞满所有东西是否有技术优势?

pa4*_*080 58

mkdir build && cd build && touch blank.txt
Run Code Online (Sandbox Code Playgroud)

在 Bash(和其他一些 shell)中&&是一个逻辑,这意味着 - 如果前一个命令返回 true,则将执行下一个命令。还有逻辑or ||。例如,您可以将这两个选项组合在一个语句中:

mkdir /tmp/abc/123 && echo 'the dir is created' || echo "the dir isn't created"
Run Code Online (Sandbox Code Playgroud)

注意,构造函数cmd_1 && cmd_2 || comd_3不能代替if; then; else语句,因为无论前面的哪个命令返回false,cmd_3都将被执行。因此,您必须小心使用它的环境。下面是一个例子:

$ true && false || echo success
success
$ false && true || echo success
success
$ false && false || echo success
success
Run Code Online (Sandbox Code Playgroud)

作为经验法则,通常,当我cd在脚本中使用命令时,我会测试目录更改是否成功:cd somewhere/ || exit. 更正确的测试提出@dessertif ! cd dir; then exit 1; fi。但在所有情况下,为了保护脚本失败,最好使用@mrks' answer 中set -e显示的选项。


mkdir build; cd build; touch blank.txt
Run Code Online (Sandbox Code Playgroud)

; 是一个行分隔符,当在一行中写入很少的分隔命令时使用它。


注意当;&&||正在使用它是不是强制性的,在上线写命令-这是中所示@同种的回答

总的来说,IMO,在一行或单独的行中编写命令之间没有任何特殊的技术优势/区别。


COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}
Run Code Online (Sandbox Code Playgroud)

这里一个或多个命令(及其参数)被分组为一个变量的值,然后这个变量(参数)通过eval. 因此,您将能够通过仅在一处更改来更改将在某个脚本中多次执行的命令。

假设您需要更改blank.txt创建文件的方式,例如,您可以通过以下方式更改相关行:

COMMAND="mkdir build && cd build && echo 'test' > blank.txt"
Run Code Online (Sandbox Code Playgroud)

实际eval优势比的使用alias是当存在重新方向,管道,逻辑运算符等正在使用中。

大多数情况下可以使用functions代替evalwhenalias不适用。此外,如果变量只包含一个命令,即CMD="cat"我们不需要eval,因为 Bash world-split 将"$CMD" "$file1" "$file2"正确扩展。

  • 测试 `cd` 成功出奇地容易跳过……但可能是非常灾难性的。 (6认同)
  • 但无论如何,我的观点是这里不需要 `eval`,因为你可以只使用 `$DIFF "$file1" "$file2"`,并让 shell 分词 `$DIFF`。只要命令和选项不包含字内空格或特殊字符,它就可以工作,通常它们不包含。如果他们这样做,那么函数会更好,或者将命令存储在数组中(参见 [此处](https://unix.stackexchange.com/a/131767/170373),在底部中间的某处;和/或 [此处](https://unix.stackexchange.com/q/444946/170373))。要么避免使用 eval 引用地狱。 (2认同)
  • A && b || c 可以以奇怪的方式执行(即不等同于 if)。还值得一提的是 set -e。 (2认同)

Bil*_*l K 13

到目前为止的答案解决了会发生什么/它是如何工作的,但我认为你在问“为什么”

这样做(对我而言)而不是在三行中输入它们的主要“原因”是有时命令需要时间(有时是几分钟)并且您不想一直闲逛。

例如,我可能会构建&&部署&&启动我的应用程序然后出去吃午饭。该过程可能需要 15 分钟才能完成。但是自从我用&&,如果构建失败,它就不会部署——这样就可以了。

预先输入是一种替代方法,但它是不确定的,您可能无法看到您输入的内容(因此会犯错误),或者程序可能会吃掉预先输入的内容(谢谢,grails!)。然后你吃完午饭回来,发现由于打字错误,你还有两个很长的任务要开始。

另一种选择是编写一个小脚本或别名。不错的主意,但不允许有很大的灵活性,就像我可以:

stop && build && test && deploy && start 
Run Code Online (Sandbox Code Playgroud)

或者我可以:

stop && start
Run Code Online (Sandbox Code Playgroud)

或者任何数量的其他组合会用一堆标志迅速污染脚本。

即使您确实编写了一个脚本,您也很可能将它与其他带有&&.

  • 关于不要等待命令完成的好点。 (2认同)

小智 10

在 Linux 上组合命令非常有用。

一个很好的例子可能是通过 ssh 重新启动远程网络接口(如果您更改了网络配置或其他内容...)。

ifdown eth0 && ifup eth0
Run Code Online (Sandbox Code Playgroud)

这可以防止您物理上访问服务器以打开您最初通过 ssh 连接到的界面。ifup eth0如果ifdown eth0单独执行,您将无法执行。)

  • 在特殊情况下;可能更安全一些,因为您不想在某些未知错误情况下跳过 ifup。 (4认同)

Ser*_*nyy 7

命令有什么作用?

为了理解原因,我们还需要了解正在做什么。脚本是连续的。在你的最后一个例子中:

mkdir build
cd build
touch blank.txt
Run Code Online (Sandbox Code Playgroud)

cd build无论mkdir build成功与否,都会被执行。当然如果mkdir fails,你会看到错误来自cd.

mkdir build; cd build; touch blank.txt是一个顺序列表。这可以被认为与多个脚本行基本相同。Shell Grammar 将对此略有不同,但结果将与上述完全相同。同样,无论先前是否成功,都会执行命令。

最后,有

mkdir build && cd build && touch blank.txt
Run Code Online (Sandbox Code Playgroud)

这是一个AND 列表- 由&&运算符分隔的一个或多个管道(或命令)的序列。此类列表中的命令以左关联性执行。这意味着,shell 将继续采用由 分隔的两个命令/管道&&,首先在左边执行一个,只有在左边一个成功时才运行右边的一个(返回零退出状态)。

在这个例子中,会发生什么?

  • shellmkdir build首先执行。
  • 以上命令成功(返回退出代码 0 ),cd build将运行
  • 再一次,让我们看看左结合性。... && touch blank.txt. 左边的东西&&成功了吗?如果是,请运行touch blank.txt

为什么使用顺序列表与 AND 列表?

mkdir build; cd build; touch blank.txt当我们对其中一个命令失败没有问题时,顺序列表是有意义的。假设mkdir build已经存在。我们仍然要cd进入build/目录和touch blank.txt. 当然,这里的缺点是有可能出现意想不到的结果。如果build没有设置执行位怎么办,这就是cd build失败的原因?该touch blank.txt会在我们当前的工作目录,而不是预期的发生build/

现在考虑mkdir build && cd build && touch blank.txt。这在某种程度上更合乎逻辑,但也有其自身的缺陷。如果build已经存在,可能有人也已经这样做了touch blank.txt,所以我们可能不想再做touch blank.txt,因为这会修改它的访问时间戳,这可能是不可取的。

结论

将命令放在同一行上取决于命令的目的和您要实现的目标。顺序列表可以简化输出,因为在命令完成之前,shell 不会重新显示提示。AND 列表允许有条件地执行命令,并可以防止执行不必要的命令。底线是用户需要知道差异以及选择什么作为任务的正确工具。


rac*_*man 5

另一个原因可能与脚本的形成方式有关:技术用户通常会构建很长的交互式命令行,反复扩展和测试它们直到它们按预期工作,然后将结果粘贴到文本文件中并添加#!/bin/bash标题超过它。有时脚本的多个部分是通过这种方法演变的。

for i ingrep | cut | sed | xargs链开头、大量使用&&||$(cat something)结构的长单行结构通常表示这种起源。