Kann ich eine umgeleitete Eingabe vor einem zusammengesetzten Befehl angeben?

8

Mit Bash können Sie vor einem Befehl eine umgeleitete Eingabe angeben:

$ <lines sed 's/^/line: /g'
line: foo
line: bar

Mit Bash können Sie Eingaben auch wie eine whileSchleife in einen zusammengesetzten Befehl umleiten :

$ while read line; do echo "line: $line"; done <lines
line: foo
line: bar

Wenn ich jedoch versuche, eine umgeleitete Eingabe vor einer whileSchleife anzugeben , wird ein Syntaxfehler angezeigt:

$ <lines while read line; do echo "line: $line"; done
bash: syntax error near unexpected token `do'

Was ist daran falsch? Ist es nicht möglich, eine umgeleitete Eingabe vor einem zusammengesetzten Befehl in Bash anzugeben? Wenn ja, warum nicht?

Stuart P. Bentley
quelle
(Ich möchte es speziell wissen, weil ich die Ausgabe eines Befehls über eine Prozessersetzung in eine while-Schleife im aktuellen Shell-Kontext leite, und ich würde es vorziehen, wenn der erste Teil des Codes nicht nach dem zweiten Teil erscheinen müsste .)
Stuart P. Bentley

Antworten:

6

Sie können in zsh, nicht in bashund choroba hat Sie bereits auf die Dokumentation hingewiesen , aber wenn Sie die Umleitung vorher haben möchten, können Sie Dinge tun wie:

< file eval '
  while IFS= read -r line; do
    ...
  done'

Oder (auf Systemen mit Unterstützung für /dev/fd/n):

< file 3<< 'EOF' . /dev/fd/3
while IFS= read -r line; do
  ...
done
EOF

(Nicht, dass du es tun möchtest).

Sie können auch tun:

exec 3< file
while IFS= read -r line <&3; do
  ...
done
exec 3<&-

(Beachten Sie, dass execdas Skript beendet wird, wenn filees nicht geöffnet werden kann.)

Oder verwenden Sie eine Funktion:

process()
  while IFS= read -r line; do
    ...
  done

< file process
Stéphane Chazelas
quelle
Ich frage mich, ob evalund /dev/fd/Routen gleich schnell sind.
Deer Hunter
1
Bei @DeerHunter wird << EOFeine temporäre Datei erstellt, abgelegt und dann geöffnet, gelesen und interpretiert, während Sie sie lesen. Es wird bestimmt weniger schnell sein, aber wahrscheinlich nicht merklich, da es sowieso alles im Gedächtnis passieren wird.
Stéphane Chazelas
"Use a function" hilft nicht wirklich - ich muss die Funktion noch vor dem Code schreiben, der in sie geleitet wird (obwohl ich wahrscheinlich eine Funktion dafür schreiben kann, mit den beiden Funktionen in jeder Reihenfolge).
Stuart P. Bentley
command execkann das Exit-Problem wie mit anderen integrierten Funktionen behandeln. aber es wird viel zu spät, um die reservierten Wortbits zu verarbeiten.
Mikesserv
@ StéphaneChazelas Was ist der Grund für die Verwendung einer temporären Datei anstelle einer Pipe?
Kasperd
3

Sie können diese Ersetzung verwenden, wenn Sie der Eingabe vorangehen möchten:

cat lines | while read line; do echo "line: $line"; done
Chaos
quelle
Ich brauche dies, um im aktuellen Shell-Kontext ausgeführt zu werden - keine Pipelines. (Der Code in der Frage war nur ein vereinfachtes Beispiel.)
Stuart P. Bentley
2

Sie können exec verwenden, um das stdin umzuleiten.

In einem Skript:

exec < <(cat lines)
while read line ; do echo "line: $line"; done

Sie können es jedoch nicht in einer Anmeldeshell verwenden (die Datei wird auf dem Standardauszug gespeichert und beendet). In diesem Fall können Sie einen anderen Dateideskriptor öffnen:

exec 3< <(cat lines)
while read -u 3 line ; do echo "line: $line"; done

Als Referenz: Mit exec

Spinne
quelle
Und wenn ich stdin für etwas anderes verwende, muss ich den Code in exec 3<&0und umgeben exec <&3, oder? (das oder verwenden Sie & 3 für die Weiterleitung, dh Ihr zweites Beispiel)
Stuart P. Bentley
Ja, Sie "speichern" den aktuellen Standard in einem anderen Dateideskriptor und können ihn nach Abschluss wiederherstellen. (Es ist gut im ersten Beispiel im verlinkten Dokument erklärt)
Spinne