Estoy intentando que mi mensaje stderr
se imprima en rojo en la terminal. El siguiente script redirige 2
a una 8
trampa 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 8
o de alguna manera no quiere imprimirse de inmediato. ¿A dónde va? redirect
se 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 8
lí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 su
ni 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 >&2
sigue 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 ( RED
y 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 -d
opción read
incorporada, que define el símbolo de final de línea. man bash
afirma esto:
-d delim The first character of delim is used to terminate the input line,
rather than newline.
Si no está definido, read
espera a \n
que aparezca para considerar una cadena como una línea. Pero cuando usa la -d
opción, puede establecerla NUL
como 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 printf
in the while
loop 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.