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 1234512345como se esperaba. Pero esto solo funciona en la implementación de coreutils de la headutilidad. 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 headlee 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 --bytesse 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 headutilidad 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+Cpara 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:
heades una de las utilidades estándar , por lo que una implementación conforme con POSIX debe implementar el comportamiento descrito anteriormente.GNU
headno 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
readretornos 17 bytes (todos la entrada disponible),headprocesa cuatro de esos y luego trata de moverse hacia atrás 13 bytes, pero no puede. (También puede ver aquí que GNUheadusa un búfer de 8 KiB).Cuando le dice
headque 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 5prueba funciona: GNUheadsolo 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 5leerá 5 bytes o un búfer completo depende de la implementación (también tenga en cuenta quehead -cno es estándar), no puede confiar en eso. Deberíadd bs=1 count=5tener una garantía de que no se leerán más de 5 bytes.-c 5descripción.headconstrucción deksh93lecturas de un byte a la vez conhead -n 1cuando la entrada no es buscable.ddsolo funciona correctamente con las tuberías conbs=1si usa uncountcomo 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, GNUddtieneiflag=fullblockeso puede aliviar eso.de POSIX
No dice nada sobre cuánto
headdebe 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
readbuiltin / utility: todos los shells que puedo encontrarreaden 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
someprogrames la misma que la del shell, pero se puede esperar quesomeprogramlea todo lo que viene después de la primera línea de entrada consumida porready no lo que queda después de una lectura almacenadaread. Por otro lado, usarheadcomo 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,readpuede 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)).headconstrucción deksh93lecturas de un byte a la vez conhead -n 1cuando la entrada no es buscable.fuente