La redirección de stdout ignora las líneas sin nueva línea

La redirección de stdout ignora las líneas sin nueva línea

Estoy intentando que mi mensaje stderrse imprima en rojo en la terminal. El siguiente script redirige 2a una 8trampa de depuración personalizada.

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "${RED}${line}${COLORRESET}"
    done
)
function undirect(){ exec 2>&9; } # reset to original 9 (==2)
function redirect(){ exec 2>&8; } # set to custom 8
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Viene deaquí, con una explicación clara.

Parece funcionar muy bien, sin embargo, la entrada que no termina en una nueva línea no se imprime en absoluto. Citando al autorevangeliosde nuevo:

bash> echo -en "hi\n" 1>&2
    hi       <-- this is red
bash> echo -en "hi" 1>&2
bash> echo -en "hi" 1>&2
bash> echo -en "hi\n" 1>&2
    hihihi   <-- this is red

No puedo entender por qué. El contenido que no es de nueva línea parece terminar en algún tipo de búfer. O ni siquiera llega al descriptor del archivo 8o de alguna manera no quiere imprimirse de inmediato. ¿A dónde va? redirectse llama correctamente cada vez. Además, IFS=''significa que no hay delimitador, por lo que no entiendo muy bien por qué el eco se produce en 8línea.

Se agradecería mucho una corrección de errores. Vinculé la respuesta citada a esta pregunta.

Toda esta solución, como señala Gilles, no es del todo perfecta. Tengo problemas con la lectura, la entrada estándar y las barras de progreso, ni puedo suni source. Y frecuentemente problemas importantes como tuberías rotas y salidas inesperadas de terminales. Si alguien llegó aquí a través de mi enlace, considere usarhttps://github.com/sickill/stderreden cambio, es mucho mejor (aún no hay problemas) (sin embargo, echo bla >&2sigue sin estar rojo yel tema respectivo está cerrado)

Respuesta1

Obtuvo la salida de líneas parciales, como parte de la misma línea en el punto donde se imprimió la nueva línea. Las partes de la línea están almacenadas dentro de read,eso es lo que hace:

ElleerLa utilidad leerá una sola línea lógica de la entrada estándar.

Por ejemplo, esto se imprime <foobar>después de un segundo, no <foo><bar>.

(echo -n foo ; sleep 1 ; echo bar) | (read x ; echo "<$x>")

Si desea capturar entradas en partes más pequeñas que líneas completas, necesitará hacer algo más, por ejemplo con Perl. Esto se imprimiría <foo><bar\n>(con la nueva línea antes de la última >, ya que, a diferencia de read, Perl no maneja especialmente la nueva línea final. No debería importar el color).

(echo -n foo ; sleep 1 ; echo bar) | 
    perl -e '$|=1; while(sysread STDIN,$a,9999) { print "<$a>"}'

Si tiene los códigos de control para los colores ( REDy COLORRESET) exportados en el entorno, puede usarlos desde el script Perl como aquí:

perl -e '$|=1; while(sysread STDIN,$a,9999) {print "$ENV{RED}$a$ENV{COLORRESET}"}'

Respuesta2

En Bash puedes usar la -dopción readincorporada, que define el símbolo de final de línea. man bashafirma esto:

-d delim    The first character of delim is used to terminate the input line, 
            rather than newline.

Si no está definido, readespera a \nque aparezca para considerar una cadena como una línea. Pero cuando usa la -dopción, puede establecerla NULcomo delimitador. Por supuesto, también necesita terminar en NUL la entrada.

Ejemplo:

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s\n" "$line"
    done

Producción:

x

y
z

Una vez más, pero ahora el printfin the whileloop no coincide con \n.

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s" "$line"
    done

Producción:

x
yz(...)

Agregué (...)y significa que no hay final de línea al final de la segunda línea. Pero el texto aún se procesa e imprime.

información relacionada