Die Pipe ist eine Datei, die in einem kernelinternen Dateisystem geöffnet wurde und nicht als reguläre Datei auf der Festplatte verfügbar ist. Es wird automatisch nur bis zu einer bestimmten Größe gepuffert und wird schließlich blockiert, wenn es voll ist. Im Gegensatz zu Dateien, die auf Blockgeräten bezogen wurden, verhalten sich Pipes sehr ähnlich wie Zeichengeräte und werden daher im Allgemeinen nicht unterstützt lseek()
und die von ihnen gelesenen Daten können nicht wie bei einer regulären Datei erneut gelesen werden.
Der Here-String ist eine reguläre Datei, die in einem gemounteten Dateisystem erstellt wird. Die Shell erstellt die Datei und behält ihren Deskriptor bei, während sie sofort die einzige Dateisystemverknüpfung entfernt (und sie so löscht), bevor sie jemals ein Byte in die Datei schreibt oder daraus liest. Der Kernel behält den für die Datei erforderlichen Speicherplatz bei, bis alle Prozesse alle Deskriptoren für die Datei freigeben. Wenn das Kind, das aus einem solchen Deskriptor liest, die Fähigkeit dazu hat, kann es mit zurückgespult lseek()
und erneut gelesen werden.
In beiden Fällen stellen die Token <<<
und |
die Dateideskriptoren dar und nicht unbedingt die Dateien selbst. Sie können eine bessere Vorstellung davon bekommen, was vor sich geht, indem Sie Dinge tun wie:
readlink /dev/fd/1 | cat
...oder...
ls -l <<<'' /dev/fd/*
Der bedeutendste Unterschied zwischen den beiden Dateien besteht darin, dass der Here-String / Doc so ziemlich eine All-at-Once-Angelegenheit ist - die Shell schreibt alle Daten hinein, bevor sie dem Kind den Read-Descriptor anbietet. Auf der anderen Seite öffnet die Shell die Pipe für die entsprechenden Deskriptoren und leitet untergeordnete Elemente ab, um diese für die Pipe zu verwalten. Sie wird also an beiden Enden gleichzeitig geschrieben / gelesen .
Diese Unterscheidungen sind jedoch nur allgemein zutreffend. Soweit ich weiß (was eigentlich gar nicht so weit ist), trifft dies auf so ziemlich jede Shell zu, die die <<<
Here-String-Kurzschrift für <<
eine Here-Document-Umleitung verarbeitet, mit der einzigen Ausnahme von yash
. yash
, busybox
, dash
Und andere ash
Varianten neigen dazu , hier-Dokumente mit Rohren zu unterstützen , aber, und so in diesen Schalen wirklich da zwischen den beiden doch sehr wenig Unterschied.
Ok - zwei Ausnahmen. Jetzt, wo ich darüber nachdenke, ksh93
mache ich eigentlich gar keine Pipe für |
, sondern kümmere mich um das ganze Geschäft mit Sockets - obwohl es eine gelöschte tmp-Datei macht, <<<*
wie die meisten anderen. Außerdem werden nur die einzelnen Abschnitte einer Pipeline in einer Subshell-Umgebung abgelegt, die eine Art POSIX-Euphemismus darstellt, da sie sich zumindest wie eine Subshell verhält , und die Gabeln auch nicht.
Tatsache ist, dass @ PSkociks Benchmark- Ergebnisse (die sehr nützlich sind) aus vielen Gründen stark variieren können und die meisten davon von der Implementierung abhängig sind. Die ${TMPDIR}
wichtigsten Faktoren für die Einrichtung des Dokuments sind der Typ des Zieldateisystems und die aktuelle Cache-Konfiguration / -Verfügbarkeit sowie die noch zu schreibende Datenmenge. Für die Pipe entspricht dies der Größe des Shell-Prozesses selbst, da für die erforderlichen Gabeln Kopien angefertigt werden. Auf diese Weise bash
ist das Einrichten von Pipelines (einschließlich Befehlsersetzungen ) furchtbar - weil es groß und sehr langsam ist, aber dabei kaum einen Unterschied macht.$(
)
ksh93
Hier ist ein weiteres kleines Shell-Snippet, um zu demonstrieren, wie eine Shell Subshells für eine Pipeline abspaltet:
pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0
32059 #bash's pid
32059 #sh's ppid
32059 #1st subshell's $$
32111 #1st subshell sh's ppid
32059 #2cd subshell's $$
32114 #2cd subshell sh's ppid
Der Unterschied zwischen dem, was ein Pipeline- pipe_who()
Aufruf meldet, und dem Bericht einer Ausführung in der aktuellen Shell beruht auf (
dem )
angegebenen Verhalten einer Subshell, die PID der übergeordneten Shell $$
beim Erweitern zu übernehmen. Obwohl bash
Subshells definitiv separate Prozesse sind, ist der $$
spezielle Shell-Parameter keine zuverlässige Quelle für diese Informationen. Die untergeordnete sh
Shell der Subshell lehnt es jedoch nicht ab , die untergeordnete Shell genau zu melden $PPID
.
Es gibt keinen Ersatz für Benchmarking:
Und für eine größere Datenmenge:
Es scheint, dass die Rohrversion höhere Einrichtungskosten hat, aber letztendlich effizienter ist.
quelle
echo foo >/dev/shm/1;cat /dev/shm/1 >/dev/null
schien in beiden Fällen schnell zu sein ...echo "$longstring"
oder<<<"$longstring"
was die Effizienz angeht, und mit kurzen Saiten spielt Effizienz sowieso keine Rolle.cat <(echo foo) >/dev/null
schneller ist alsecho foo | cat >/dev/null
.