Was ist der Unterschied zwischen <<
, <<<
und < <
in bash?
Was ist der Unterschied zwischen <<
, <<<
und < <
in bash?
Antworten:
Hier dokumentieren
<<
ist als here-document
Struktur bekannt. Sie lassen das Programm wissen, was der Endtext sein wird, und wann immer dieses Trennzeichen angezeigt wird, liest das Programm alle Informationen, die Sie dem Programm als Eingabe gegeben haben, und führt eine Aufgabe darauf aus.
Folgendes meine ich:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
In diesem Beispiel weisen wir das wc
Programm an, auf eine EOF
Zeichenfolge zu warten , dann fünf Wörter EOF
einzugeben und anschließend zu signalisieren, dass die Eingabe abgeschlossen ist. In der Tat ist es ähnlich, als würde man wc
von selbst laufen , Wörter eingeben und dann drückenCtrlD
In der Bash werden diese über temporäre Dateien implementiert, normalerweise in der Form /tmp/sh-thd.<random string>
, während sie in Strichen als anonyme Pipes implementiert werden. Dies kann über Tracing-Systemaufrufe mit strace
Befehl beobachtet werden . Ersetzen Sie bash
durch, um sh
zu sehen, wie /bin/sh
diese Umleitung ausgeführt wird.
$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'
Hier String
<<<
ist bekannt als here-string
. Anstatt Text einzugeben, geben Sie einem Programm eine vorgefertigte Textfolge. Mit einem solchen Programm, wie bc
wir es tun können, bc <<< 5*4
um nur die Ausgabe für diesen speziellen Fall zu erhalten, muss bc nicht interaktiv ausgeführt werden.
Hier werden Zeichenfolgen in bash über temporäre Dateien implementiert, in der Regel im Format /tmp/sh-thd.<random string>
, die später nicht verknüpft werden. Dadurch belegen sie vorübergehend Speicherplatz, werden jedoch nicht in der Liste der /tmp
Verzeichniseinträge angezeigt und existieren effektiv als anonyme Dateien, die möglicherweise noch vorhanden sind Der Dateideskriptor wird von der Shell selbst über den Dateideskriptor referenziert. Dieser Dateideskriptor wird vom Befehl geerbt und später über die dup2()
Funktion auf den Dateideskriptor 0 (stdin) dupliziert . Dies kann über beobachtet werden
$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd
Und über Tracing-Systemaufrufe (Ausgabe aus Gründen der Lesbarkeit gekürzt; beachten Sie, wie temporäre Dateien als fd 3 geöffnet werden, Daten darauf geschrieben werden, dann mit dem O_RDONLY
Flag fd 4 erneut geöffnet werden und später die Verknüpfung dup2()
auf fd 0 aufheben, die cat
später vererbt wird ):
$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4) = 4
[pid 10229] write(3, "\n", 1) = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0) = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072) = 5
[pid 10229] write(1, "TEST\n", 5TEST
) = 5
[pid 10229] read(0, "", 131072) = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Meinung: Da Zeichenfolgen hier möglicherweise temporäre Textdateien verwenden, ist dies der mögliche Grund, warum Zeichenfolgen hier immer eine abschließende neue Zeile einfügen, da die Textdatei nach POSIX-Definition Zeilen haben muss, die mit einem Zeilenumbruch enden.
Prozessersetzung
Wie tldp.org erklärt,
Die Prozessersetzung speist die Ausgabe eines Prozesses (oder von Prozessen) in den Standard eines anderen Prozesses ein.
Tatsächlich ist dies also vergleichbar mit der Weiterleitung von stdout von einem Befehl zum anderen, z echo foobar barfoo | wc
. Beachten Sie jedoch : In der bash-Manpage sehen Sie, dass es als gekennzeichnet ist <(list)
. Grundsätzlich können Sie also die Ausgabe mehrerer (!) Befehle umleiten.
Hinweis: Wenn Sie sagen, dass < <
Sie sich nicht auf eine Sache beziehen, sondern auf zwei Umleitungen mit Einzel- <
und Prozessumleitung der Ausgabe von <( . . .)
.
Was passiert nun, wenn wir nur die Substitution durchführen?
$ echo <(echo bar)
/dev/fd/63
Wie Sie sehen, erstellt die Shell einen temporären Dateideskriptor, /dev/fd/63
in dem die Ausgabe gespeichert wird (was laut Gilles 'Antwort eine anonyme Pipe ist). Das bedeutet, <
dass der Dateideskriptor als Eingabe in einen Befehl umgeleitet wird.
Ein sehr einfaches Beispiel wäre, die Ausgabe von zwei Echo-Befehlen durch einen Prozess in wc zu ersetzen:
$ wc < <(echo bar;echo foo)
2 2 8
Hier lassen wir die Shell einen Dateideskriptor für alle Ausgaben in Klammern erstellen und leiten diese als Eingabe an. wc
Wie erwartet empfängt wc diesen Stream von zwei Echo-Befehlen, die für sich genommen zwei Zeilen mit jeweils einem Wort ausgeben. und passend haben wir 2 Wörter, 2 Zeilen und 6 Zeichen plus zwei gezählte neue Zeilen.
Randnotiz : Die Prozessersetzung kann als Bashismus bezeichnet werden (ein Befehl oder eine Struktur, die in fortgeschrittenen Shells verwendet werden kann bash
, aber nicht von POSIX angegeben wurde), wurde jedoch ksh
vor der Bash-Existenz als ksh-Manpage implementiert und diese Antwort schlägt vor. Shells mögen tcsh
und haben mksh
jedoch keine Prozessersetzung. Wie können wir also die Ausgabe mehrerer Befehle in einen anderen Befehl umleiten, ohne den Prozess zu ersetzen? Gruppierung plus Verrohrung!
$ (echo foo;echo bar) | wc
2 2 8
Tatsächlich ist dies dasselbe wie im obigen Beispiel. Dies unterscheidet sich jedoch unter der Haube von der Prozessersetzung, da wir die gesamte Unterschale und wc
die mit dem Rohr verbundene Länge ausfüllen . Auf der anderen Seite bewirkt die Prozessersetzung, dass ein Befehl einen temporären Dateideskriptor liest.
Wenn wir also eine Gruppierung mit Rohrleitungen durchführen können, warum brauchen wir dann eine Prozessersetzung? Weil wir manchmal keine Rohrleitungen verwenden können. Betrachten Sie das folgende Beispiel - Vergleichen der Ausgaben von zwei Befehlen mit diff
(für die zwei Dateien erforderlich sind, und in diesem Fall geben wir zwei Dateideskriptoren an).
diff <(ls /bin) <(ls /usr/bin)
< <
wird verwendet, wenn eine Prozessersetzung stdin liefert . Ein solcher Befehl aussehen könnte: cmd1 < <(cmd2)
. Zum Beispielwc < <(date)
< <
ist keine Sache für sich, im Falle einer Prozesssubstitution <
folgt lediglich etwas anderes, das gerade beginnt<
<<<
wurde es zuerst von der Unix-Portierung von Plan 9 RC-Shell implementiert und später von zsh, bash und ksh93 übernommen. Ich würde es dann nicht einen Bashismus nennen.
echo 'foo' | read; echo ${REPLY}
Wird nicht zurückgegeben foo
, da sie read
in einer Unterschale gestartet werden - Pipes starten eine Unterschale. Wird jedoch read < <(echo 'foo'); echo ${REPLY}
korrekt zurückgegeben foo
, da keine Unterschale vorhanden ist.
< <
ist ein Syntaxfehler:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
ist Prozessersetzung ( <()
) kombiniert mit Umleitung ( <
):
Ein ausgedachtes Beispiel:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
Bei der Prozessersetzung wird der Pfad zum Dateideskriptor wie ein Dateiname verwendet. Wenn Sie einen Dateinamen nicht direkt verwenden möchten (oder können), kombinieren Sie die Prozessersetzung mit der Umleitung.
Klar, es gibt keinen < <
Operator.
<()
gibt eine dateinamenähnliche Sache an, daher ist es im Allgemeinen nützlich < <()
, das Standardverzeichnis dort zu ersetzen, wo es möglicherweise nicht erforderlich ist. In wc
geschieht letzteres mehr nützlich zu sein. Es könnte an anderer Stelle weniger nützlich sein
< <
Wenn es sich um einen Syntaxfehler handelt, meinen Sie wahrscheinlich, dass command1 < <( command2 )
dies eine einfache Eingabeumleitung gefolgt von einer Prozessersetzung ist, die sehr ähnlich, aber nicht äquivalent ist zu:
command2 | command1
Der Unterschied, vorausgesetzt Sie laufen, bash
wird command1
im zweiten Fall in einer Subshell ausgeführt, während er im ersten Fall in der aktuellen Shell ausgeführt wird. Das bedeutet, dass die eingegebenen Variablen command1
bei der Prozessersetzungsvariante nicht verloren gehen.
< <
wird einen Syntaxfehler geben. Die bestimmungsgemäße Verwendung ist wie folgt:
Erklären anhand von Beispielen:
Beispiel für < <()
:
while read line;do
echo $line
done< <(ls)
Im obigen Beispiel stammt die Eingabe für die while-Schleife von dem ls
Befehl, der zeilenweise gelesen und echo
in der Schleife bearbeitet werden kann.
<()
wird für die Prozesssubstitution verwendet. Weitere Informationen und Beispiele <()
finden Sie unter diesem Link: