warum ist `test.sh & | test.sh` falsch

warum ist `test.sh & | test.sh` falsch

Ich habe ein Bash-Skript mit test.shdem folgenden Namen:

#!/bin/bash

while :
do
    echo xxx
    sleep 1
done

Ich weiß nicht, warum ./test.sh & | ./test.shmir ein Fehler angezeigt wird: -bash: syntax error near unexpected token |', während ./test.sh | ./test.shund ./test.sh | ./test.sh &funktionieren.

Antwort1

Das ist einfach nicht erlaubt durch die ShellGrammatik:

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

Beachten Sie, dass &oder ;nur Listen beenden kann und Listen nur Teil eines Complete_Commands sind, der nicht Teil anderer Produktionen sein kann.

Das ist nur eine grammatikalische Eigenart. Genauso wie { foo & }semantisch ähnlich zu ist foo &(die Klammern werden im Gegensatz zu runden Klammern nur zum Gruppieren von Befehlen verwendet, sie erstellen selbst weder eine Untershell noch irgendeine Art von Bereich), können Sie es { foo & } | barals semantisch ähnlich zu betrachten foo & | bar, wenn Letzteres von der Grammatik zugelassen würde.

Der Workaround für Ihr Beispiel ist also einfach:

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

Wenn Sie das von einer interaktiven Shell aus ausführen, beachten Sie, dass der &Willenichtein ... kreierenHintergrundjob, sondern führen Sie den Befehl einfach asynchron aus, da die rechte Seite einer Pipeline immer in einer Subshell ausgeführt wird und die Befehle aus einer Subshell mit deaktivierter Jobsteuerung ausgeführt werden.

Dies ist ähnlich wie:

(sleep 3600 &)

Die asynchronen Listen (Befehle, die durch beendet werden &) in einer Subshell werden mit ihrer Standardeingabe umgeleitet /dev/nullund ihre SIGINTund SIGQUITSignale werden ignoriert. Sie werden weiterhin im Hintergrund ausgeführt, aber Sie können sie nicht mit fg, disownthem usw. in den Vordergrund holen, wie Sie es bei regulären Jobs tun können. Außerdem werden Sie nicht benachrichtigt, wenn sie gestoppt oder beendet wurden.

In einem Skript macht dies absolut keinen Unterschied, da Skripte mit deaktivierter Jobsteuerung ausgeführt werden.

Wenn Sie aber die Ausgabe eines Hintergrunds weiterleiten möchtenArbeitin den VordergrundArbeitin einem (ninteraktivShell, die einzige portable Lösung, die mir einfällt, ist die Verwendung einer benannten Pipe.

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

verwandte Informationen