Der folgende Shell-Befehl sollte nur ungerade Zeilen des Eingabestreams ausgeben:
echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)
Aber anstatt es druckt nur die erste Zeile: aaa
.
Dasselbe passiert nicht, wenn es mit der Option -c
( --bytes
) verwendet wird:
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)
Dieser Befehl wird 1234512345
wie erwartet ausgegeben. Dies funktioniert jedoch nur in der coreutils- Implementierung des head
Dienstprogramms. Bei der Belegtbox- Implementierung werden immer noch zusätzliche Zeichen verwendet, sodass die Ausgabe gerecht ist 12345
.
Ich denke, diese spezielle Art der Implementierung dient Optimierungszwecken. Sie können nicht wissen, wo die Zeile endet, sodass Sie nicht wissen, wie viele Zeichen Sie lesen müssen. Die einzige Möglichkeit, keine zusätzlichen Zeichen aus dem Eingabestream zu verbrauchen, besteht darin, den Stream Byte für Byte zu lesen. Das Lesen eines Bytes aus dem Stream kann jedoch langsam sein. Ich vermute also, dass head
der Eingabestream in einen ausreichend großen Puffer eingelesen wird und dann die Zeilen in diesem Puffer gezählt werden.
Dies gilt nicht für den Fall, dass die --bytes
Option verwendet wird. In diesem Fall wissen Sie, wie viele Bytes Sie lesen müssen. Sie können also genau diese Anzahl von Bytes und nicht mehr lesen. Die corelibs- Implementierung nutzt diese Gelegenheit, die busybox jedoch nicht. Sie liest immer noch mehr Byte als erforderlich in einen Puffer. Dies wird wahrscheinlich zur Vereinfachung der Implementierung durchgeführt.
Also die Frage. Ist es richtig, dass das head
Dienstprogramm mehr Zeichen aus dem Eingabestream verbraucht, als angefordert wurden? Gibt es einen Standard für Unix-Dienstprogramme? Und wenn ja, gibt es dieses Verhalten an?
PS
Sie müssen drücken Ctrl+C
, um die obigen Befehle zu stoppen. Die Unix-Dienstprogramme schlagen beim Lesen darüber hinaus nicht fehl EOF
. Wenn Sie nicht drücken möchten, können Sie einen komplexeren Befehl verwenden:
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)
was ich der Einfachheit halber nicht benutzt habe.
quelle
Antworten:
Ja, das ist erlaubt (siehe unten).
Ja, POSIX Volume 3, Shell & Utilities .
In seiner Einführung macht es:
head
ist eines der Standard-Dienstprogramme , daher muss eine POSIX-konforme Implementierung das oben beschriebene Verhalten implementieren.GNU
head
ist versuchen , den Dateideskriptor in die richtige Position zu verlassen, aber es ist unmöglich , an Rohren zu suchen, so in Ihrem Test fehlschlägt er die Position wiederherzustellen. Sie können dies sehen mitstrace
:Der
read
gibt 17 Bytes zurück (alle verfügbaren Eingaben),head
verarbeitet vier davon und versucht dann, 13 Bytes zurückzusetzen, kann dies aber nicht. (Sie können hier auch sehen, dass GNUhead
einen 8-KiB-Puffer verwendet.)Wenn Sie festlegen
head
, dass Bytes gezählt werden sollen (was nicht dem Standard entspricht), weiß das System, wie viele Bytes gelesen werden sollen, sodass es (wenn es auf diese Weise implementiert wird) den Lesevorgang entsprechend einschränken kann. Dies ist der Grund, warum Ihrhead -c 5
Test funktioniert: GNUhead
liest nur fünf Bytes und muss daher nicht versuchen, die Position des Dateideskriptors wiederherzustellen.Wenn Sie das Dokument in eine Datei schreiben und diese stattdessen verwenden, erhalten Sie das gewünschte Verhalten:
quelle
line
(inzwischen von POSIX / XPG entfernten, aber auf vielen Systemen noch verfügbaren) oderread
(IFS= read -r line
) Dienstprogramme verwenden, die jeweils ein Byte lesen, um das Problem zu vermeiden.head -c 5
von der Implementierung abhängt , ob 5 Byte oder ein vollständiger Puffer gelesen wird (beachten Sie auch, dass dieshead -c
nicht der Standard ist). Darauf können Sie sich nicht verlassen. Sie benötigendd bs=1 count=5
eine Garantie, dass nicht mehr als 5 Bytes gelesen werden.-c 5
Beschreibung aktualisiert .head
Funktion jeweilsksh93
ein Byte liest,head -n 1
wenn die Eingabe nicht suchbar ist.dd
funktioniert nur mit Pipes korrekt,bs=1
wenn Sie einencount
as read für Pipes verwenden. Möglicherweise wird weniger als angefordert zurückgegeben (aber mindestens ein Byte, es sei denn, eof wird erreicht). GNUdd
hat,iflag=fullblock
dass dies jedoch gelindert werden kann.von POSIX
Es sagt nichts darüber aus, wie viel
head
von der Eingabe gelesen werden muss. Es wäre dumm, es Byte für Byte zu lesen, da es in den meisten Fällen extrem langsam wäre.Dies wird jedoch im
read
Dienstprogramm builtin / angesprochen : Alle Shells, die ich inread
Pipes finden kann, sind byteweise und der Standardtext kann so interpretiert werden, dass dies getan werden muss, um nur diese eine einzelne Zeile lesen zu können:Im Fall von
read
, der in Shell-Skripten verwendet wird, wäre ein häufiger Anwendungsfall etwa so:Hier ist die Standardeingabe von
someprogram
die gleiche wie die der Shell, aber es ist zu erwarten, dasssomeprogram
alles gelesen wird, was nach der ersten von derread
belegten Eingabezeile kommt, und nicht alles, was nach einem gepufferten Lesen von übrig geblieben istread
. Andererseits ist die Verwendung vonhead
wie in Ihrem Beispiel viel seltener.Wenn Sie wirklich jede zweite Zeile löschen möchten, ist es besser (und schneller), ein Tool zu verwenden, das die gesamte Eingabe auf einmal verarbeiten kann, z
quelle
-r
,read
möglicherweise mehr als eine Zeile lesen (ohneIFS=
dass auch führende und nachfolgende Leerzeichen und Tabulatoren entfernt würden (mit dem Standardwert von$IFS
)).head
Funktion jeweilsksh93
ein Byte liest,head -n 1
wenn die Eingabe nicht suchbar ist.quelle