Ich versuche zu verstehen, wie genau Bash die folgende Zeile behandelt:
$(< "$FILE")
Laut der Bash-Manpage entspricht dies:
$(cat "$FILE")
und ich kann der Argumentationslinie für diese zweite Linie folgen. Bash führt eine Variablenerweiterung durch $FILE
, gibt die Befehlssubstitution ein, übergibt den Wert von $FILE
an cat
, cat gibt den Inhalt der $FILE
Standardausgabe aus, die Befehlssubstitution wird beendet, indem die gesamte Zeile durch die Standardausgabe ersetzt wird, die sich aus dem Befehl im Inneren ergibt, und Bash versucht, sie wie folgt auszuführen ein einfacher Befehl.
Für die erste Zeile, die ich oben erwähnt habe, verstehe ich es jedoch wie folgt: Bash führt eine Variablensubstitution durch $FILE
, Bash öffnet $FILE
zum Lesen der Standardeingabe, irgendwie wird die Standardeingabe in die Standardeingabe kopiert , die Befehlssubstitution wird beendet und Bash versucht, den resultierenden Standard auszuführen Ausgabe.
Kann mir bitte jemand erklären, wie der Inhalt von $FILE
von stdin zu stdout geht?
quelle
bash
wird das als interpretierencat filename
", meinen Sie, dass dieses Verhalten spezifisch für die Befehlssubstitution ist? Denn wenn ich< filename
alleine renne, macht Bash es nicht fertig. Es wird nichts ausgegeben und ich kehre zu einer Eingabeaufforderung zurück.cat < filename
zucat filename
dem ich entgegensetzen und zurückkommen kann.|
erstellt eine Pipe zwischen zwei Unterprozessen (oder bei einigen Shells von einem Unterprozess zur Standardeingabe der Shell). Der Shell-Operator$(…)
erstellt eine Pipe von einem Unterprozess zur Shell selbst (nicht zu ihrer Standardeingabe). Der Shell-Operator<
enthält keine Pipe, sondern öffnet nur eine Datei und verschiebt den Dateideskriptor zur Standardeingabe.< file
ist nicht dasselbe wiecat < file
(außerzsh
wo es ist$READNULLCMD < file
).< file
ist perfekt POSIX und öffnet sich nurfile
zum Lesen und macht dann nichts (file
ist also sofort in der Nähe). Es ist$(< file)
oder`< file`
ist ein spezieller Operator vonksh
,zsh
undbash
(und das Verhalten wird in POSIX nicht angegeben). Siehe meine Antwort für Details.$(cmd1) $(cmd2)
normalerweise dasselbe wie$(cmd1; cmd2)
. Aber schauen Sie sich den Fall an, wocmd2
ist< file
. Wenn wir sagen$(cmd1; < file)
, wird die Datei nicht gelesen, aber mit$(cmd1) $(< file)
. Es ist also falsch zu sagen, dass dies$(< file)
nur ein gewöhnlicher Fall$(command)
mit einem Befehl von ist< file
.$(< …)
ist ein Sonderfall der Befehlssubstitution und keine normale Verwendung der Umleitung.$(<file)
(funktioniert auch mit`<file`
) ist ein spezieller Operator der Korn-Shell, der vonzsh
und kopiert wirdbash
. Es sieht sehr nach Befehlsersetzung aus, ist es aber nicht wirklich.In POSIX-Shells lautet ein einfacher Befehl:
Alle Teile sind optional. Sie können nur Umleitungen, nur Befehle, nur Zuweisungen oder Kombinationen verwenden.
Wenn es Umleitungen, aber keinen Befehl gibt, werden die Umleitungen ausgeführt (also
> file
würde a geöffnet und abgeschnittenfile
), aber dann passiert nichts. SoÖffnet sich
file
zum Lesen, aber dann passiert nichts, da es keinen Befehl gibt. Alsofile
ist das dann geschlossen und das wars. Wenn$(< file)
es sich um eine einfache Befehlsersetzung handelt , wird sie zu nichts erweitert.In der POSIX-Spezifikation führt in
$(script)
, wenn esscript
nur aus Umleitungen besteht, zu nicht spezifizierten Ergebnissen . Das soll das besondere Verhalten der Kornschale ermöglichen.In ksh (hier getestet mit
ksh93u+
), wenn das Skript aus einem und nur einem einfachen Befehl besteht (obwohl Kommentare vorher und nachher zulässig sind), der nur aus Umleitungen besteht (kein Befehl, keine Zuweisung) und wenn die erste Umleitung ein stdin (fd) ist 0) einspeisen (<
,<<
oder<<<
) die Umleitung, so:$(< file)
$(0< file)
$(<&3)
(auch$(0>&3)
eigentlich da das eigentlich der gleiche Operator ist)$(< file > foo 2> $(whatever))
aber nicht:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
dann
<&3
) abzüglich der nachfolgenden Zeilenumbruchzeichen.als ob mit
$(cat < file)
außer demcat
$(<${file=foo.txt})
oder$(<file$((++n)))
) , da der darin enthaltene Code nicht in einer Unterschale ausgeführt wird.In
zsh
, ist es die gleiche , außer dass das spezielle Verhalten nur dann ausgelöst wird , wenn gibt es nur eine Datei - Eingabe - Umleitung (<file
oder0< file
, nein<&3
,<<<here
,< a < b
...)Außer beim Emulieren anderer Shells in:
Das ist , wenn es nur Eingang Umleitungen ohne Befehle ist, außerhalb des Befehls Substitution,
zsh
führt die$READNULLCMD
(einen Pager durch Standard), und , wenn es sowohl Eingangs- und Ausgang Umleitungen, die$NULLCMD
(cat
Standardeinstellung), so dass selbst wenn$(<&3)
nicht als die besonderen erkannte Operator, es wird immer noch wie in funktionieren,ksh
indem ein Pager aufgerufen wird, um dies zu tun (dieser Pager verhält sich so,cat
da sein Standard eine Pipe ist).Doch während
ksh
‚s$(< a < b)
auf den Inhalt erweitern würdea
, inzsh
, dehnt es sich auf den Inhalta
undb
(oder nur ,b
wenn diemultios
Option deaktiviert ist),$(< a > b)
würde kopierena
zub
und erweitern , um nichts, usw.bash
hat einen ähnlichen Operator, aber mit ein paar Unterschieden:Kommentare sind vorher erlaubt, aber nicht danach:
funktioniert aber:
dehnt sich zu nichts aus.
Wie in
zsh
, nur eine Datei stdin Umleitung, obwohl es kein Zurückgreifen auf a$READNULLCMD
gibt$(<&3)
,$(< a < b)
führen Sie die Umleitungen durch, aber erweitern Sie auf nichts.bash
zwar nicht aufgerufencat
, aber dennoch ein Prozess gegabelt, der den Inhalt der Datei durch eine Pipe führt, wodurch die Optimierung wesentlich geringer ist als in anderen Shells. Es ist in der Tat wie ein$(cat < file)
Wocat
wäre ein eingebautescat
.$(<${file=foo.txt})
oben genannten Fällen geht beispielsweise diese$file
Zuordnung nachträglich verloren).In
bash
,IFS= read -rd '' var < file
(funktioniert auch inzsh
) ist eine effektivere Methode, um den Inhalt einer Textdatei in eine Variable einzulesen . Es hat auch den Vorteil, dass die nachgestellten Zeilenumbrüche beibehalten werden. Siehe auch$mapfile[file]
inzsh
(imzsh/mapfile
Modul und nur für reguläre Dateien), das auch mit Binärdateien funktioniert.Beachten Sie, dass die pdksh-basierten Varianten von
ksh
im Vergleich zu ksh93 einige Variationen aufweisen. Von Interesse inmksh
(einer dieser von pdksh abgeleiteten Muscheln), inwird dahingehend optimiert, dass der Inhalt des hier vorliegenden Dokuments (ohne die nachgestellten Zeichen) erweitert wird, ohne dass eine temporäre Datei oder Pipe verwendet wird, wie dies sonst bei den hier verwendeten Dokumenten der Fall ist, was es zu einer effektiven mehrzeiligen Anführungszeichen-Syntax macht.
Für alle Versionen von und portierbar zu sein
ksh
, bedeutet , sich darauf zu beschränken, nur Kommentare zu vermeiden und zu berücksichtigen, dass Änderungen an Variablen, die innerhalb von vorgenommen wurden, beibehalten werden können oder nicht.zsh
bash
$(<file)
quelle
$(<)
es sich um einen Operator für Dateinamen handelt? Ist<
in$(<)
einem Umleitungsoperator oder keinen Operator auf seinem eigenen, und muss Teil des gesamten Operators sein$(<)
?$(<file)
soll auffile
ähnliche Weise auf den Inhalt von erweitert werden, wie dies der Fall$(cat < file)
wäre. Wie es gemacht wird, variiert von Shell zu Shell, was in der Antwort ausführlich beschrieben wird. Wenn Sie möchten, können Sie sagen, dass es sich um einen speziellen Operator handelt, der ausgelöst wird, wenn eine Befehlssubstitution (syntaktisch) eine einzelne Standardumleitung (syntaktisch) enthält, jedoch wiederum mit Einschränkungen und Variationen, die von der hier aufgeführten Shell abhängen .n<&m
undn>&m
das Gleiche tun? Das wusste ich nicht, aber ich denke, es ist nicht allzu überraschend.dup(m, n)
. Ich kann einige Hinweise darauf sehen, dass ksh86 stdio verwendet, und einigefdopen(fd, "r" or "w")
, also könnte es dann wichtig gewesen sein. Die Verwendung von stdio in einer Shell macht jedoch wenig Sinn, sodass ich nicht erwarte, dass Sie eine moderne Shell finden, in der dies einen Unterschied macht. Ein Unterschied besteht darin , dass>&n
istdup(n, 1)
(kurz für1>&n
), während<&n
istdup(n, 0)
(kurz für0<&n
).dup2()
;dup()
nimmt nur ein Argument und verwendet wieopen()
der niedrigste verfügbare Dateideskriptor. (Heute habe ich erfahren, dass es einedup3()
Funktion gibt .)Weil
bash
es intern für Sie erledigt, den Dateinamen erweitert und die Datei auf Standardausgabe katzt, wie wenn Sie es tun würden$(cat < filename)
. Es ist eine Bash-Funktion. Vielleicht müssen Sie sich denbash
Quellcode ansehen , um genau zu wissen, wie er funktioniert.Hier ist die Funktion, um diese Funktion zu handhaben (Aus
bash
Quellcode, Dateibuiltins/evalstring.c
):Eine Notiz,
$(<filename)
die nicht genau gleichbedeutend ist mit$(cat filename)
; Letzteres schlägt fehl, wenn der Dateiname mit einem Bindestrich beginnt-
.$(<filename)
war ursprünglich vonksh
und wurde hinzugefügtbash
vonBash-2.02
.quelle
cat filename
schlägt fehl, wenn der Dateiname mit einem Bindestrich beginnt, da cat Optionen akzeptiert. Mit den meisten modernen Systemen können Sie das umgehencat -- filename
.Stellen Sie sich die Befehlsersetzung so vor, als würden Sie einen Befehl wie gewohnt ausführen und die Ausgabe an dem Punkt ausgeben, an dem Sie den Befehl ausführen.
foo=$(echo "bar")
setzt den Wert der Variablen$foo
aufbar
; die Ausgabe des Befehlsecho bar
.Befehlsersetzung
quelle
$(< file)
bezieht sich auf den Sonderfall von , und er benötigt kein Tutorial zum allgemeinen Fall. Wenn Sie sagen, dass dies$(< file)
nur ein gewöhnlicher Fall$(command)
mit einem Befehl von ist< file
, dann sagen Sie dasselbe, was Adam Katz sagt , und Sie liegen beide falsch.