Se esperaba que el siguiente comando de shell imprimiera solo líneas impares de la secuencia de entrada:
echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)
Pero en vez de eso sólo imprime la primera línea: aaa
.
No ocurre lo mismo cuando se usa con la opción -c
( --bytes
):
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)
Este comando sale 1234512345
como se esperaba. Pero esto solo funciona en la implementación de coreutils de la head
utilidad. La implementación de busybox todavía consume caracteres adicionales, por lo que la salida es justa 12345
.
Supongo que esta forma específica de implementación se realiza con fines de optimización. No puede saber dónde termina la línea, por lo que no sabe cuántos caracteres necesita leer. La única forma de no consumir caracteres adicionales de la secuencia de entrada es leer la secuencia byte por byte. Pero leer de la transmisión un byte a la vez puede ser lento. Supongo que head
lee el flujo de entrada en un búfer lo suficientemente grande y luego cuenta las líneas en ese búfer.
No se puede decir lo mismo del caso cuando --bytes
se usa la opción. En este caso, usted sabe cuántos bytes necesita leer. Entonces puede leer exactamente este número de bytes y no más que eso. La implementación de corelibs usa esta oportunidad, pero la busybox no, todavía lee más bytes de los necesarios en un búfer. Probablemente se haga para simplificar la implementación.
Entonces la pregunta. ¿Es correcto que la head
utilidad consuma más caracteres de la secuencia de entrada de los que se le pidió? ¿Existe algún tipo de estándar para las utilidades de Unix? Y si lo hay, ¿especifica este comportamiento?
PD
Debe presionar Ctrl+C
para detener los comandos anteriores. Las utilidades de Unix no fallan al leer más allá EOF
. Si no desea presionar, puede usar un comando más complejo:
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)
que no usé por simplicidad.
fuente
Respuestas:
Sí, está permitido (ver más abajo).
Sí, POSIX volumen 3, Shell y utilidades .
Lo hace, en su introducción:
head
es una de las utilidades estándar , por lo que una implementación conforme con POSIX debe implementar el comportamiento descrito anteriormente.GNU
head
no tratan de dejar el descriptor de archivo en la posición correcta, pero es imposible que buscar en las tuberías, por lo que en su prueba de que no puede restaurar la posición. Puedes ver esto usandostrace
:Los
read
retornos 17 bytes (todos la entrada disponible),head
procesa cuatro de esos y luego trata de moverse hacia atrás 13 bytes, pero no puede. (También puede ver aquí que GNUhead
usa un búfer de 8 KiB).Cuando le dice
head
que cuente bytes (que no es estándar), sabe cuántos bytes leer, por lo que puede (si se implementa de esa manera) limitar su lectura en consecuencia. Es por eso que suhead -c 5
prueba funciona: GNUhead
solo lee cinco bytes y, por lo tanto, no necesita buscar restaurar la posición del descriptor de archivo.Si escribe el documento en un archivo y lo usa en su lugar, obtendrá el comportamiento que busca:
fuente
line
(ahora eliminadas de POSIX / XPG pero aún disponibles en muchos sistemas) oread
(IFS= read -r line
) que leen un byte a la vez para evitar el problema.head -c 5
leerá 5 bytes o un búfer completo depende de la implementación (también tenga en cuenta quehead -c
no es estándar), no puede confiar en eso. Deberíadd bs=1 count=5
tener una garantía de que no se leerán más de 5 bytes.-c 5
descripción.head
construcción deksh93
lecturas de un byte a la vez conhead -n 1
cuando la entrada no es buscable.dd
solo funciona correctamente con las tuberías conbs=1
si usa uncount
como las lecturas en las tuberías pueden devolver menos de lo solicitado (pero al menos un byte a menos que se alcance eof). Sin embargo, GNUdd
tieneiflag=fullblock
eso puede aliviar eso.de POSIX
No dice nada sobre cuánto
head
debe leerse de la entrada. Exigirle que lea byte a byte sería una tontería, ya que sería extremadamente lento en la mayoría de los casos.Sin embargo, esto se aborda en el
read
builtin / utility: todos los shells que puedo encontrarread
en las tuberías de un byte a la vez y el texto estándar puede interpretarse en el sentido de que esto debe hacerse, para poder leer solo esa línea:En el caso de
read
, que se usa en scripts de shell, un caso de uso común sería algo como esto:Aquí, la entrada estándar de
someprogram
es la misma que la del shell, pero se puede esperar quesomeprogram
lea todo lo que viene después de la primera línea de entrada consumida porread
y no lo que queda después de una lectura almacenadaread
. Por otro lado, usarhead
como en su ejemplo es mucho más infrecuente.Si realmente desea eliminar cualquier otra línea, sería mejor (y más rápido) usar alguna herramienta que pueda manejar toda la entrada de una sola vez, por ej.
fuente
-r
,read
puede leer más de una línea (sinIFS=
ella también se eliminarían los espacios y las pestañas iniciales y finales (con el valor predeterminado de$IFS
)).head
construcción deksh93
lecturas de un byte a la vez conhead -n 1
cuando la entrada no es buscable.fuente