Hier sind einige Dinge zu beachten.
i=`cat input`
kann teuer sein und es gibt viele Variationen zwischen den Schalen.
Dies ist eine Funktion, die als Befehlsersetzung bezeichnet wird. Die Idee ist, die gesamte Ausgabe des Befehls abzüglich der nachfolgenden Zeilenumbrüche in der i
Variablen im Speicher zu speichern.
Dazu geben Shells den Befehl in einer Subshell an und lesen seine Ausgabe über eine Pipe oder ein Socket-Paar. Sie sehen hier eine Menge Variationen. In einer 50-MB-Datei sehe ich beispielsweise, dass Bash 6-mal so langsam ist wie ksh93, aber etwas schneller als zsh und doppelt so schnell wie yash
.
Der Hauptgrund dafür bash
, dass es langsam ist, ist, dass es 128 Bytes gleichzeitig aus der Pipe liest (während andere Shells 4 KB oder 8 KB gleichzeitig lesen) und durch den Systemaufruf-Overhead bestraft wird.
zsh
Nachbearbeitung erforderlich, um NUL-Bytes zu umgehen (andere Shells setzen NUL-Bytes außer Kraft), und yash
noch umfangreichere Nachbearbeitung durch Analysieren von Multi-Byte-Zeichen.
Alle Shells müssen die nachfolgenden Zeilenumbrüche entfernen, was sie möglicherweise mehr oder weniger effizient ausführen.
Einige möchten möglicherweise mit NUL-Bytes eleganter umgehen als andere und auf Vorhandensein prüfen.
Sobald Sie diese große Variable im Arbeitsspeicher haben, müssen Sie bei jeder Manipulation im Allgemeinen mehr Arbeitsspeicher zuweisen und Daten übertragen.
Hier übergeben Sie (wollten) den Inhalt der Variablen an echo
.
Zum Glück echo
ist es in Ihrer Shell eingebaut, sonst wäre die Ausführung wahrscheinlich mit einem zu langen Fehler fehlgeschlagen . Selbst dann wird das Erstellen des Argumentlistenarrays möglicherweise das Kopieren des Inhalts der Variablen umfassen.
Das andere Hauptproblem bei Ihrem Befehlssubstitutionsansatz besteht darin, dass Sie den Operator split + glob aufrufen (indem Sie vergessen, die Variable in Anführungszeichen zu setzen).
Dazu Schalen müssen die Zeichenfolge als eine Folge von behandeln Zeichen (obwohl einige Granaten nicht und sind fehlerhaft in dieser Hinsicht) so in UTF-8 - Locales, dass Mittel UTF-8 - Sequenzen Parsen (falls noch nicht geschehen , wie der yash
Fall ist) Suchen Sie nach $IFS
Zeichen in der Zeichenfolge. Wenn $IFS
Leerzeichen, Tabulatoren oder Zeilenumbrüche enthalten sind (was standardmäßig der Fall ist), ist der Algorithmus noch komplexer und teurer. Dann müssen die aus dieser Aufteilung resultierenden Wörter zugewiesen und kopiert werden.
Der Glob-Teil wird noch teurer. Wenn eines dieser Wörter glob Zeichen ( *
, ?
, [
), dann wird die Shell den Inhalt einiger Verzeichnisse lesen und einige teure Musterabgleich tun ( bash
s Implementierung zum Beispiel ist bekanntlich sehr schlecht dazu).
Wenn die Eingabe so etwas enthält /*/*/*/../../../*/*/*/../../../*/*/*
, ist das extrem teuer, da das bedeutet, dass Tausende von Verzeichnissen aufgelistet werden und sich diese auf mehrere Hundert MiB erweitern können.
Dann echo
wird in der Regel eine zusätzliche Verarbeitung durchgeführt. Einige Implementierungen erweitern \x
Sequenzen in dem Argument, das sie erhalten, was bedeutet, dass der Inhalt und wahrscheinlich eine andere Zuordnung und Kopie der Daten analysiert werden.
Auf der anderen Seite ist OK in den meisten Shells cat
nicht integriert. Dies bedeutet, dass Sie einen Prozess forken und ausführen (also den Code und die Bibliotheken laden), aber nach dem ersten Aufruf diesen Code und den Inhalt der Eingabedatei wird im Speicher zwischengespeichert. Auf der anderen Seite wird es keinen Vermittler geben. cat
liest große Mengen gleichzeitig und schreibt sie sofort ohne Verarbeitung, und es muss keine große Menge an Speicher reserviert werden, nur der eine Puffer, den es wiederverwendet.
Dies bedeutet auch, dass es viel zuverlässiger ist, da es nicht an NUL-Bytes verstopft und nachfolgende Zeilenumbrüche nicht abschneidet Erweitern Sie die Escape-Sequenz, obwohl Sie dies vermeiden können, indem Sie printf
anstelle von echo
) verwenden.
Wenn Sie es weiter optimieren möchten, anstatt es cat
mehrmals aufzurufen , übergeben Sie es einfach input
mehrmals an cat
.
yes input | head -n 100 | xargs cat
Führt 3 anstelle von 100 Befehlen aus.
Um die variable Version zuverlässiger zu machen, müssen Sie Folgendes verwenden zsh
(andere Shells können mit NUL-Bytes nicht umgehen):
zmodload zsh/mapfile
var=$mapfile[input]
repeat 10 print -rn -- "$var"
Wenn Sie wissen, dass die Eingabe keine NUL-Bytes enthält, können Sie dies POSIX-zuverlässig tun (obwohl dies möglicherweise nicht funktioniert, wenn printf
keine eingebauten Daten vorliegen) mit:
i=$(cat input && echo .) || exit # add an extra .\n to avoid trimming newlines
i=${i%.} # remove that trailing dot (the \n was removed by cmdsubst)
n=10
while [ "$n" -gt 10 ]; do
printf %s "$i"
n=$((n - 1))
done
Aber das wird niemals effizienter als die Verwendung cat
in der Schleife (es sei denn, die Eingabe ist sehr klein).
cat $(for i in $(seq 1 10); do echo "input"; done) >> output
? :)