我是 Linux 和 Bash 脚本的新手。在工作中,我见过具有类似结构的 Bash 脚本:
mkdir build && cd build && touch blank.txt
或者:
mkdir build; cd build; touch blank.txt
甚至是异国情调:
COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}
最后一个示例给出了一个可能的用例,其中单行可能有用,但通常以下内容更易于阅读并且(至少对我而言)允许您直观地调试脚本:
mkdir build
cd build
touch blank.txt
把所有东西都塞在一条线上有技术优势吗?
在 Bash(以及其他一些 shell 和大多数高级编程语言)
&&
中是一个逻辑and,这意味着 - 如果上一个命令返回 true,则将执行下一个命令。还有逻辑或||
。例如,您可以将这两个选项组合在一个语句中:注意,构造
cmd_1 && cmd_2 || comd_3
不是if.. then.. else
语句的替代,因为无论前面的哪个命令返回false,cmd_3
都会执行。因此,您必须小心使用它的环境。这是一个例子:作为经验法则,通常,当我
cd
在脚本中使用命令时,我会测试目录更改是否成功:cd somewhere/ || exit
. @dessert 提出了更合适的if ! cd dir; then exit 1; fi
测试:但在所有情况下,为了保护脚本失败,最好使用@mrks 答案set -e
中显示的选项。;
是一个行分隔符,当在一行中写入几个单独的命令时使用它。请注意,在使用
;
或&&
和时||
,不必将命令写在一行 - 这在@allo 的答案中进行了说明。完全,IMO,在一行或单独的行中编写命令之间没有任何特殊的技术优势/差异。
这里一个或多个命令(及其参数)被分组为一个变量的值,然后这个变量(参数)通过
eval
. 因此,您将能够通过仅在一处更改来更改将在某个脚本中多次执行的命令。假设您需要更改
blank.txt
创建文件的方式,例如,您可以通过以下方式更改相关行:实际使用
eval
的优势alias
在于,当有重定向、管道、逻辑运算符- 在所有控制流运算符 - 正在使用时。在大多数情况下,您可以使用
functions
而不是eval
whenalias
不适用。此外,如果变量只包含一个命令,即CMD="cat"
我们不需要eval
,因为 Bash 分词将"$CMD" "$file1" "$file2"
正确扩展。下面是一个示例,其中
eval
最简单的方法是执行一些命令自动化:跟踪“在最后一小时从日志文件中写入的行”是否可能?本节的先前版本(在评论中讨论)可在此处获得。
到目前为止的答案解决了发生了什么/它是如何工作的,但我认为你在问“为什么”
这样做的主要“原因”(对我来说)而不是在三行上键入它们是有时命令需要时间(有时是几分钟)并且您不想一直闲逛。
例如,我可能会构建
&&
deploy&&
启动我的应用程序,然后出去吃午饭。该过程可能需要 15 分钟才能完成。但是,由于我使用&&
了 ,因此如果构建失败,它将不会部署——这样就可以了。Type-ahead 是一种替代方案,但它是不确定的,您可能无法看到您输入的内容(因此会出错),或者程序可能会吃掉 type-ahead(谢谢,grails!)。然后你吃完午饭回来,发现你还有两个很长的任务要开始,因为一个错字。
另一种选择是编写一个小脚本或别名。不错的主意,但不允许有太多的灵活性,就像我可以:
或者我可以:
或者任何数量的其他组合会很快用一堆标志污染脚本。
即使您确实编写了一个脚本,您也很可能将它与其他脚本与
&&
.在 Linux 上组合命令非常有用。
一个很好的例子可能是通过 ssh 重新启动远程网络接口(如果您更改了网络配置或其他东西......)。
这可以防止您物理上访问服务器以调出您最初 ssh 到的界面。(如果单独执行,您将无法执行。)
ifup eth0
ifdown eth0
命令有什么作用?
为了了解原因,我们还需要了解正在做什么。脚本是连续的。在你的最后一个例子中:
cd build
不管mkdir build
成功与否都会被执行。当然如果mkdir fails
,你会看到错误来自cd
.mkdir build; cd build; touch blank.txt
是一个顺序列表。这可以被认为与多个脚本行基本相同。Shell Grammar 将对此略有不同,但结果将与上述完全相同。同样,无论先前是否成功,都会执行命令。最后,还有
这是一个AND 列表- 由运算符分隔的一个或多个管道(或命令)的序列
&&
。此类列表中的命令以左关联性执行。这意味着,shell 将继续采用两个由 分隔的命令/管道&&
,首先在左侧执行一个,只有在左侧成功时才运行右侧的一个(返回零退出状态)。在这个例子中,会发生什么?
mkdir build
首先执行。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 列表允许有条件地执行命令,并且可以防止执行不必要的命令。底线是用户需要知道差异以及选择什么作为任务的正确工具。
另一个原因可能是脚本是如何产生的:技术用户构建非常长的交互式命令行并反复扩展和测试它们直到它们按需要工作,然后将结果粘贴到文本文件中并拍打
#!/bin/bash
标题并不罕见超过它。有时,脚本的多个片段是通过该方法演变而来的。for i in
以,链开头的长一行结构grep | cut | sed | xargs
,大量使用&&
和||
,$(cat something)
结构通常表明了这种起源。为什么我们有时喜欢使用它
当我们在Ask Ubuntu中在这里写答案时,将它们放在一行中是很有帮助的,人们可以更轻松地剪切并粘贴到他们的终端中。例如:
如前所述,您可以使用
&&
相同的效果,但如果出现上述错误,您最终可能会进入错误的目录。在这种情况下;
,最好&&
不要测试成功:为什么我们必须使用它
在某些命令中,您必须将多行串在一起。一种情况是缩写
if
-commands-fi
必须是一行。例如:这只是风格问题。没有技术优势。
我认为其他人很好地解释了这三行的区别,但请注意,这并不意味着您需要将它们放在一行上。
做同样的事情
foo && bar
。你仍然需要小心,因为不一样,但运行 foo 然后产生语法错误。大多数情况下的一般规则:当 shell 从语法中知道您的行不完整时,它允许您继续下一行。
当第一行可以按原样执行时,它将被执行(下一行可能是也可能不是语法错误,因为缺少第一行)。
当您仍然希望脚本更具可读性时,可以像这样转义换行符
确保字符后没有空格
\
,因为它必须直接位于换行符之前。这里的许多答案都谈到了安全性,例如,“如果
cd
失败会发生什么”?虽然结合诸如
cd foo && rm *
解决该问题的命令,但有一种更好的方法:采取这个文件夹结构:
和以下脚本:
由于
notexisting
不存在,rm *
将运行.
并删除do_not_delete_me
。但是,如果您使用 启动脚本
set -e
,它将因第一个失败的命令而中止:./test.sh: line 4: cd: notexisting: No such file or directory
如评论中所述,将其包含在 shebang: 中是有意义的
#!/bin/bash -e
。