Ich möchte eine große Datei aufteilen und GZipen und diese Antwort schien das zu sein, wonach ich suchte, und es schien mir eine sehr nützliche Möglichkeit zu sein, Dinge zu tun, an die ich nie gedacht hatte, also möchte ich es verallgemeinern; Das einzige Problem ist: Es scheint nicht zu funktionieren.
Angenommen, ich möchte meine Eingabe aufteilen und weiterverarbeiten (ich weiß split
aber ich möchte es direkt in meinem Skript herumspielen!)
Dies nutzt read
eine Zeile in eine Variable einlesen
#!/bin/bash
printf " a \n b \n c \n d " |
for ((i = 0 ; i < 2 ; i++)) ; do
echo "<< $i >>"
for ((j = 0 ; j < 2 ; j++)) ; do
read l
echo "$l"
done
done
Es druckt
<< 0 >>
a
b
<< 1 >>
c
d
Was ist fast das, was ich will, abgesehen von der Tatsache, dass die Leerzeichen von Anfang bis Ende gekürzt werden (und die Zeile möglicherweise auf andere Weise geändert wird? Funktioniert es mit willkürlichem UTF-8-codiertem Inhalt?) bearbeiten gelöst
Und ich stelle mir vor, es könnte ziemlich langsam sein. bearbeiten Benchmarking: mindestens 3000x langsamer.
Also habe ich versucht, es durchzuleiten head
(Ich bekomme das Ergebnis mit awk
wie die Antwort schon sagt, scheint es nichts anders zu machen)
#!/bin/bash
printf " a \n b \n c \n d " |
for ((i = 0 ; i < 2 ; i++)) ; do
echo "<< $i >>"
head -n 2
done
Das druckt
<< 0 >>
a
b
<< 1 >>
Und hört da auf head
schließt offenbar seine Eingabe beim Beenden. Ich habe kein Programm gefunden, das dies nicht tut, und vielleicht wird es tatsächlich vom System durchgesetzt? (Ich bin auf OS X)
Verwenden head -n 2 <&0
was (laut den bash docs) den file descriptor zuerst kopiert, funktioniert auch nicht.
Muss ich eine Named Pipe verwenden? Gibt es eine Beschwörung, um diese Arbeit zu machen?
echo "..$l.."
um zu sehen, was in gespeichert wurde l , wie Echo ignoriert führende und nachfolgende Leerzeichen.read
streift es ab,echo ".$l."
druckt.a.
. Ich denke, die Shell entfernt Leerzeichen beim Aufteilen der Argumente,l=" a "; echo $l
druckta
aberl=" a "; echo "$l"
druckt die Leerzeichen `a`. (read
kann auch die Eingabe auch durch Leerzeichen und mehrere Variablen füllen, deshalb wahrscheinlich)read l
.Antworten:
Das Problem ist hier nicht genau das
head
oderawk
sind "Schließen der Eingabe". Sie haben keine Wahl; Jedes Programm schließt seine Eingabe, wenn es beendet wird. Dies wird vom Betriebssystem erzwungen.Das Problem ist, dass die Standardeingabe eine Pipe ist und die Programme gepufferte Lesevorgänge ausführen. Es gibt keine Möglichkeit, Daten aus einer Pipe ungelesen zu lassen. Alle Daten, die sich im Readahead befinden, gehen verloren. Wenn Sie anstelle einer Pipe eine Datei verwenden, werden Sie wahrscheinlich feststellen, dass diese gut funktioniert:
Zumindest funktioniert das auf Ubuntu gut. Sie können es mit einer Pipe zum Laufen bringen, wenn Sie die Pufferung ausschalten - aber das macht die Sache sehr langsam. Hier ist ein kleines C-Programm, das die Pufferung abschaltet und dann die Eingabe zeichenweise wiederholt, bis die angeforderte Anzahl von Zeilen verbraucht ist:
Das hat für mich gut funktioniert (wieder auf Ubuntu - und Sie müssen es mit kompilieren
-std=c99
oder-std=c11
damit sich der Compiler nicht beschwert). Es ist wahr, dass das Programm nicht aufruftfclose(stdin)
, aber das Hinzufügen macht keinen Unterschied. Auf der anderen Seite entfernen Sie den Anruf ansetvbuf
wird Sie wahrscheinlich wieder zu dem Symptom bringen, bei dem Sie es beobachtet habenhead
. (Und es wird auch das Programm zum Laufen bringen Menge schneller.)Wenn du GNU hättest
split
Anstelle der BSD-Version, die mit OS X ausgeliefert wird, können Sie die nützliche verwenden--filter=COMMAND
Syntax, die ziemlich genau das tut, was Sie wollen; Anstatt geteilte Dateien zu erstellen, leitet er jeden Dateibereich an einen Aufruf des angegebenen Befehls weiter (und setzt die Umgebungsvariable$FILE
auf den erwarteten Dateinamen).quelle
--filter
; Das ist die Art von Flexibilität, die ich gesucht habe.Durch Angabe einer Variablen zu
read
Sie bestellen es, um eine Wortteilung durchzuführen. Tun Sie das nicht, und Leerzeichen bleiben unberührt:Ausgabe:
Es scheint sehr einfach zu sein, aber tatsächlich haben Sie eine sehr gute Frage gestellt, da diese Funktion im Mann nicht klar erklärt wird.
P. S. Ich würde ein verwenden
-r
Flagge (nicht behandeln\
als Fluchtzeichen) fürread
ebenfalls.quelle
$REPLY
. Die Alternative, die ich gesehen habe, istIFS= read var
. Jedoch mitread
In einer Bash-Schleife ist es unglaublich langsam (1e3-Zeilen dauern 6,3 Sekunden) im Vergleich zuhead
(1e6-Zeilen dauern 1,8 Sekunden), daher ist es nur für kleine Dateien nützlich.Wenn Sie jedoch ein eigenständiges Skript zum Bearbeiten großer Dateien schreiben möchten, ist AWK aus Effizienzgründen viel besser geeignet als Bash. Ein Einlage:
Das gleiche wie ein Skript:
Das gleiche wie ein Bash-Skript:
Ein Benchmark mit einer Million Zeilen:
Sie sehen, warum Bash hier kein bevorzugter Dolmetscher ist.
quelle