¿Por qué es `test.sh & | test.sh` incorrecto

¿Por qué es `test.sh & | test.sh` incorrecto

Tengo un script bash llamado test.sha continuación:

#!/bin/bash

while :
do
    echo xxx
    sleep 1
done

No sé por qué ./test.sh & | ./test.shme da error: -bash: syntax error near unexpected token |', mientras que ./test.sh | ./test.shy ./test.sh | ./test.sh &funcionan.

Respuesta1

Eso simplemente no está permitido por el caparazón.gramática:

%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     : '&'
                 | ';'
                 ;

Observe cómo &or ;solo puede terminar listas, y las listas son solo parte de un comando_completo, que no puede ser parte de otras producciones.

Eso es sólo una peculiaridad gramatical; así como { foo & }es semánticamente similar a foo &(las llaves, a diferencia de los paréntesis, solo se usan para agrupar comandos, no crean por sí mismas un subshell ni ningún tipo de alcance), puedes considerar { foo & } | barque es semánticamente similar a foo & | bar, si este último estuviera permitido por la gramática.

Entonces la solución para tu ejemplo es simplemente:

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

Si lo ejecuta desde un shell interactivo, observe que el &testamentonocrear untrabajo de fondo, pero simplemente ejecute el comando de forma asincrónica, porque el lado derecho de una canalización siempre se ejecuta en un subshell y los comandos de un subshell se ejecutan con el control de trabajo deshabilitado.

Esto es similar a:

(sleep 3600 &)

Las listas asincrónicas (comandos terminados en &) en un subshell se ejecutan con su entrada estándar redirigida /dev/nully sus señales SIGINTy SIGQUITignoradas. Continuarán ejecutándose en segundo plano, pero no podrá ponerlos en primer plano con fg, disownetc., como puede hacer con los trabajos normales. Además, no se le notificará cuando fueron detenidos o cancelados.

En un script esto no supone ninguna diferencia, ya que los scripts se ejecutan con el control de trabajo desactivado.

Pero si desea canalizar la salida de un fondotrabajoa un primer planotrabajoen uninteractivoshell, la única solución portátil que se me ocurre es utilizar una canalización con nombre.

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

información relacionada