如何在Bash中一行命令中运行多个后台命令?

294

我通常使用以下方式运行多个命令:

sleep 2 && sleep 3
或者
sleep 2 ; sleep 3

但如果我想从一个命令行命令中同时将它们都在后台运行怎么办?

sleep 2 & && sleep 3 &

不起作用。使用 ; 替换 && 也不行。

有没有什么方法可以做到这一点?


12
使用 () 包围命令。 - nhahtdh
7个回答

524

你希望它们如何运行?如果你希望它们在后台启动并依次运行,你可以这样做:

{ sleep 2; sleep 3; } &

如果你希望sleep 3仅在sleep 2成功运行后才运行,则:

sleep 2 && sleep 3 &

如果您希望它们同时在后台运行,那么可以这样做:

sleep 2 & sleep 3 &

两种技术可以结合使用,例如:

{ sleep 2; echo first finished; } & { sleep 3; echo second finished; } &

由于其本质,Bash通常有许多不同的技术来完成相同的任务,尽管它们之间有时存在细微差异。


2
如果我们执行 sleep 2 && sleep 3 & 会发生什么?我在bash中尝试了一下,似乎以相同的方式工作(使用echo测试)。 - Wajahat
@HakanBaba,是的,它确实会在后台创建一个子shell。 - Douwe van der Leest
3
你试过执行 sleep 2 && echo "foo" && sleep 3 & echo "bar" 吗?&& 运算符告诉 shell 在执行后面的命令之前先执行它前面的命令,只有在前一个命令没有任何错误地执行完毕时,shell 才会执行后续命令。这与 ; 完全不同,因为 ; 让后续命令执行,不管前一个命令是否成功,除非出现致命错误。& 告诉 shell 在后台执行所有“连接”的命令,这里的“连接”指的是命令是管道 (|) 或逻辑运算符的一部分。 - Douwe van der Leest
在Bash手册中,这些列表运算符中,“&&”和“||”具有相等的优先级,其次是“;”和“&”,它们也具有相等的优先级。 - rosshjb
由于某些原因,以下代码无法正常工作 - i=0;while [ $i -le 10 ]; do echo i:$i;i=$[$i+1];sleep 2;done; & j=10;while [ $j -ge 0 ]; do echo j:$j;j=$[$j-1];sleep 2;done; & 或者 (i=0;while [ $i -le 10 ]; do echo i:$i;i=$[$i+1];sleep 2;done; &) ; (j=10;while [ $j -ge 0 ]; do echo j:$j;j=$[$j-1];sleep 2;done; &),会出现 -bash: syntax error near unexpected token `&' 错误。我是否漏掉了什么? - Ira
这个不起作用。我的第一个命令运行了 rdiff-backup,我想在命令完成后卸载 source。我像上面那样将命令放在后台,写入日志文件,并在脚本的下一行显示一个日志 tail -f logfile。现在,如果我取消 tail,它也会取消我放到后台的 rdiff-backup。我看到这是因为日志将显示一个 SIGTERM。 - bomben

67

你需要在你最后的版本中加入一些括号 --

(sleep 2 &) && (sleep 3 &)

或者这个也可以 --

(sleep 2 &) ; (sleep 3 &)

2
注意:当列表与&一起使用时,其退出状态始终为零。因此,上述两种情况执行相同的操作(shell异步地依次执行两边)。 - rosshjb

11

要运行多个后台命令,您需要在每个命令的末尾添加&。 例如: (command1 &) && (command2 &) && (command3 &)


&&连接bg'ed命令没有任何作用,因为它们立即返回零。也就是说,这与使用;相同。OP想要的是(command 1 && command 2) & - André Chalella

7
上面的答案使用了括号。Bash也可以使用大括号来达到类似的目的:
{ sleep 2 && sleep 3; } &

请注意,大括号对语法更为挑剔--需要在 { 后面加上空格,在 } 前面加上空格,以及最后一个分号是必需的。在某些情况下,大括号更有效,因为它们不会产生一个新的子shell。在这种情况下,我不知道是否有任何区别。

1
{ sleep 2; sleep 3; } & 怎么样? - Kenny Evitt

5
你可以使用bash命令替换$(command),如下所示:
$(command1 ; command2) &

请注意,标准输入和标准输出仍然链接到父进程,重定向至少标准输出可能会有些棘手。因此,您可以在单行中链接命令,然后将字符串传递给bash命令,以生成一个新的进程来处理执行。
bash -c "command1 ; command2" & 

当你需要在bash脚本中运行多个命令时,这种方法尤其有用。

这两个语句应该是等效的。在这两种情况下,都会产生一个进程来处理命令(命令链),并且在末尾添加&将执行分离。

这次,在命令的末尾添加&>/dev/null可以将stdout重定向到/dev/null,避免父进程的stdout输出。例如:

bash -c "command1 ; command2" &>/dev/null &

3

这是有效的:

$(sleep 2 &) && sleep 3 &

此外,您还可以执行以下操作:

$(sleep 2 && sleep 3) &

1
我也有同样的任务。我尝试了 (sleep2 ; fg )& sleep3 ; fg,它有效。当你按下ctrl+c两次时,这两个进程可以停止。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接