为什么是`test.sh & | test.sh`错误

为什么是`test.sh & | test.sh`错误

我有一个 bash 脚本,名称test.sh如下:

#!/bin/bash

while :
do
    echo xxx
    sleep 1
done

我不知道为什么./test.sh & | ./test.sh给我一个错误:-bash: syntax error near unexpected token |',而./test.sh | ./test.sh./test.sh | ./test.sh &工作。

答案1

shell 根本不允许这样做语法:

%start  complete_command
%%
complete_command : list separator
                 | list
                 ;
list             : list separator_op and_or
                 |                   and_or
                 ;
...

pipe_sequence    :                             command
                 | pipe_sequence '|' linebreak command
                 ;
...
separator_op     : '&'
                 | ';'
                 ;

请注意如何&;只能终止列表,并且列表只是complete_command的一部分,而不能是其他产生式的一部分。

这只是一个语法上的怪癖;正如{ foo & }在语义上类似于foo &(大括号与圆括号不同,仅用于对命令进行分组,它们本身不会创建子shell或任何类型的范围),如果允许后者,您可以认为{ foo & } | bar在语义上类似于foo & | bar通过语法。

所以你的例子的解决方法就是:

{ ./test.sh & } | ./test.sh

如果您从交互式 shell 运行它,请&注意不是创建一个后台工作,但只需异步运行命令,因为管道的右侧始终在子 shell 中运行,并且子 shell 中的命令在禁用作业控制的情况下运行。

这类似于:

(sleep 3600 &)

子 shell 中的异步列表(以 终止的命令&)运行时,它们的 stdin 被重定向/dev/null,并且它们的SIGINTSIGQUIT信号被忽略。它们将继续在后台运行,但您不能像处理常规作业那样使用 、they 等fg将它们带到前台。disown此外,当它们被停止或终止时,您也不会收到通知。

在脚本中,这绝对没有区别,因为脚本是在禁用作业控制的情况下运行的。

但是如果你想通过管道输出背景工作到前台工作在一个交互的shell,我能想到的唯一可移植的解决方案是使用命名管道。

$ mkfifo fifo
$ command | filter1 > fifo &  # this is the background job
$ < fifo sed 10q | filter2    # this is the foreground job

相关内容