echo que sale a stderr

1116

¿Existe una herramienta Bash estándar que actúe como eco pero que se envíe a stderr en lugar de stdout?

Sé que puedo hacerlo, echo foo 1>&2pero es un poco feo y, sospecho, propenso a errores (por ejemplo, es más probable que se edite mal cuando las cosas cambian).

BCS
fuente
Relacionado: stackoverflow.com/a/12704488/194894
Flujo el

Respuestas:

1454

Podrías hacer esto, lo que facilita la lectura:

>&2 echo "error"

>&2copia el descriptor de archivo # 2 al descriptor de archivo # 1. Por lo tanto, después de realizar esta redirección, ambos descriptores de archivo se referirán al mismo archivo: el descriptor de archivo número 2 al que originalmente se refería. Para obtener más información, consulte el Tutorial ilustrado de redireccionamiento de Bash Hackers .

Marco Aurelio
fuente
2
Aprendí este truco hace bastante tiempo. Esta página tiene buena información. tldp.org/LDP/abs/html/io-redirection.html
Marco Aurelio
46
@ BCS No sé sobre el uso aliasde un script de shell. Probablemente sería más seguro usarloerrcho(){ >&2 echo $@; }
Braden Best
3
> & 2 normalmente se pone al final. Esto funcionará, pero se usa con menos frecuencia
Iskren Ivov Chernev
159
En los casi 40 años que llevo usando sistemas similares a Unix, nunca se me ha ocurrido que podría colocar la redirección en cualquier lugar, pero al final. Ponerlo por adelantado de esta manera lo hace mucho más obvio (o "facilita la lectura" como dice @MarcoAurelio). +1 por enseñarme algo nuevo.
Hefesto
FYI: si desea formatear o hacer algo además de simplemente hacer eco de la cadena, tendrá que mover la redirección de nuevo al final. Por ejemplo errcho(){ >&2 echo $@|pr -To5;}no funcionará. Para hacer algo así, tendrá que colocar la redirección en algún lugar después de la última tubería como:errcho(){ echo $@|>&2 pr -To5;}
Jon Red
424

Podrías definir una función:

echoerr() { echo "$@" 1>&2; }
echoerr hello world

Esto sería más rápido que un script y no tendría dependencias.

La sugerencia específica de bash de Camilo Martin utiliza una "cadena aquí" e imprimirá todo lo que le pase, incluidos los argumentos (-n) que el eco normalmente se tragaría:

echoerr() { cat <<< "$@" 1>&2; }

La solución de Glenn Jackman también evita el problema de tragar el argumento:

echoerr() { printf "%s\n" "$*" >&2; }
James Roth
fuente
77
Debo decir que el eco no es confiable. echoerr -ne xtno va a imprimir "-ne xt". Mejor uso printfpara eso.
Camilo Martin
10
Oh, también puedes usar gato:echoerr() { cat <<< "$@" 1>&2; }
Camilo Martin
2
No estaba al tanto de eso. Adicional.
James Roth
44
O bien,printf "%s\n" "$*" >&2
Glenn Jackman
44
@GKFX Por supuesto, solo funciona correctamente al citar. Por qué la gente no cita sus cadenas está más allá de mí. (cuando no cita, todo lo separado por uno o más $IFSespacios en blanco se envía como un argumento separado, que en el caso de echoconcatenarlos con 0x20s, pero los peligros de no citar mucho más que la conveniencia de escribir 2 caracteres menos) .
Camilo Martin
252

Como 1es la salida estándar, no tiene que nombrarla explícitamente delante de una redirección de salida como, >sino que simplemente puede escribir:

echo Este mensaje va a stderr> & 2

Dado que parece preocupado porque 1>&2le resultará difícil escribir de manera confiable, ¡la eliminación del redundante 1podría ser un pequeño estímulo para usted!

Brandon Rhodes
fuente
59

Otra opción

echo foo >>/dev/stderr
Steven Penny
fuente
44
¿Es esta opción portátil? ¿Alguien sabe si esto no funciona para un poco de sabor de Unix?
Dacav
77
No funciona en ciertos chroots, que no pueden acceder a / dev / stderr.
Zachary Vance
12
Si la secuencia de comandos que se ejecuta esta línea - llamémosle foo- tiene su propia stderr redirigido - por ejemplo foo >foo.log 2>&1- a continuación, echo foo >/dev/stderrse clobber toda la salida antes de ella. >>debería usarse en su lugar:echo foo >>/dev/stderr
doshea
Del mismo modo, tienes /dev/fd/2.
jbruni
@Dacav esto es seguro portátil: /proc/self/fd/2. Vea mi respuesta a continuación :)
Sebastian
32

No, esa es la forma estándar de hacerlo. No debería causar errores.

Matthew Flaschen
fuente
99
No debería causar errores, pero es más probable que lo haga. OTOH no es tan importante.
BCS
66
@ Mike DeSimone: si alguien más se mete con el código, baraja la salida y no sabe realmente bash, podría soltar (o escribir mal) fácilmente 1>&2. Todos deseamos que esto no suceda, pero estoy seguro de que todos hemos estado en lugares donde sucede.
Cascabel
2
( echo something 1>&2 ; something else ) > log-> (echo something; cp some junk 1>&2 ; something else) > logVaya.
BCS
30
En mi humilde opinión, si alguien se mete con el código y no sabe bash, este puede ser el menor de sus problemas.
Mike DeSimone
8
Creo que si eso puede ser un problema, deberías comenzar a usar un idioma diferente: tratar de hacer bash a prueba de tontos es una aventura de tontos.
intuido
17

Si no le importa registrar el mensaje también en syslog, la forma no_so_ fea es:

logger -s $msg

La opción -s significa: "Enviar el mensaje al error estándar, así como al registro del sistema".

Grzegorz Luczywo
fuente
1
¡esto es genial! ¿Qué tan portátil es?
code_monk
@code_monk: se espera que el comando logger sea compatible con IEEE Std 1003.2 ("POSIX.2"). El comando logger es parte del paquete util-linux y está disponible en Linux Kernel Archive ⟨kernel.org/ pub / linux / utils / util- linux⟩.
miku
12

Esta es una función STDERR simple, que redirige la entrada de la tubería a STDERR.

#!/bin/bash
# *************************************************************
# This function redirect the pipe input to STDERR.
#
# @param stream
# @return string
#
function STDERR () {

cat - 1>&2

}

# remove the directory /bubu
if rm /bubu 2>/dev/null; then
    echo "Bubu is gone."
else
    echo "Has anyone seen Bubu?" | STDERR
fi


# run the bubu.sh and redirect you output
tux@earth:~$ ./bubu.sh >/tmp/bubu.log 2>/tmp/bubu.err
erselbst
fuente
2
Creo que puedes hacer lo mismo con un alias y ser mucho más compacto
BCS
o simplemente puede canalizar el archivo del dispositivo directamente echo what | /dev/stderr...
nuevo
11

Nota: estoy respondiendo la pregunta posterior, no la pregunta confusa / vaga "echo que sale a stderr" (ya respondida por OP).

Use una función para mostrar la intención y obtener la implementación que desea. P.ej

#!/bin/bash

[ -x error_handling ] && . error_handling

filename="foobar.txt"
config_error $filename "invalid value!"

output_xml_error "No such account"

debug_output "Skipping cache"

log_error "Timeout downloading archive"

notify_admin "Out of disk space!"

fatal "failed to open logger!"

Y error_handlingsiendo:

ADMIN_EMAIL=root@localhost

config_error() { filename="$1"; shift; echo "Config error in $filename: $*" 2>&1; }

output_xml_error() { echo "<error>$*</error>" 2>&1; }

debug_output() { [ "$DEBUG"=="1" ] && echo "DEBUG: $*"; }

log_error() { logger -s "$*"; }

fatal() { which logger >/dev/null && logger -s "FATAL: $*" || echo "FATAL: $*"; exit 100; }

notify_admin() { echo "$*" | mail -s "Error from script" "$ADMIN_EMAIL"; }

Razones que manejan las preocupaciones en OP:

  • mejor sintaxis posible (palabras significativas en lugar de símbolos feos)
  • Es más difícil cometer un error (especialmente si reutiliza el script)
  • no es una herramienta Bash estándar, pero puede ser una biblioteca shell estándar para usted o su empresa / organización

Otras razones:

  • claridad - muestra intención a otros mantenedores
  • velocidad: las funciones son más rápidas que las secuencias de comandos de shell
  • reutilización: una función puede llamar a otra función
  • configurabilidad: no es necesario editar el script original
  • depuración: es más fácil encontrar la línea responsable de un error (especialmente si está bloqueando con una tonelada de salida de redireccionamiento / filtrado)
  • robustez: si falta una función y no puede editar el script, puede recurrir al uso de una herramienta externa con el mismo nombre (por ejemplo, log_error puede ser alias al registrador en Linux)
  • Cambio de implementaciones: puede cambiar a herramientas externas eliminando el atributo "x" de la biblioteca
  • agnóstico de salida: ya no tiene que preocuparse si va a STDERR u otro lugar
  • personalización: puede configurar el comportamiento con variables de entorno
Cezary Baginski
fuente
10

Mi sugerencia:

echo "my errz" >> /proc/self/fd/2

o

echo "my errz" >> /dev/stderr

echo "my errz" > /proc/self/fd/2lo hará de manera efectiva a la salida stderr, porque /proc/selfes un enlace con el proceso actual, y /proc/self/fdejerce en el proceso abierto descriptores de archivo, y luego, 0, 1, y 2representar stdin, stdouty stderrrespectivamente.

El /proc/selfenlace no funciona en MacOS, sin embargo, /proc/self/fd/*está disponible en Termux en Android, pero no /dev/stderr. ¿Cómo detectar el sistema operativo desde un script Bash? puede ayudar si necesita hacer que su script sea más portátil determinando qué variante usar.

Sebastian
fuente
55
El /proc/selfenlace no funciona en MacOS, así que me quedaré con el /dev/stderrmétodo más directo . Además, como se señaló en otras respuestas / comentarios, probablemente sea mejor usarlo >>para agregar.
MarkHu
44
/proc/self/fd/*está disponible en Termux en Android, pero no /dev/stderr.
go2null
9

No lo use, catya que algunos se mencionan aquí. cates un programa while echoy printfson bash (shell) integradas. Lanzar un programa u otro script (también mencionado anteriormente) significa crear un nuevo proceso con todos sus costos. Usando las funciones integradas, las funciones de escritura son bastante baratas, porque no hay necesidad de crear (ejecutar) un proceso (entorno).

El operador pregunta "¿hay alguna herramienta estándar para enviar ( tubería ) a stderr", la respuesta corta es: NO ... ¿por qué? ... redirigir tuberías es un concepto elemental en sistemas como unix (Linux ...) y bash (sh) se basa en estos conceptos.

Estoy de acuerdo con el abridor de que la redirección con anotaciones como esta: &2>1no es muy agradable para los programadores modernos, pero eso es bash. Bash no estaba destinado a escribir programas grandes y robustos, está destinado a ayudar a los administradores a llegar a trabajar con menos pulsaciones de teclas ;-)

Y al menos, puede colocar la redirección en cualquier lugar de la línea:

$ echo This message >&2 goes to stderr 
This message goes to stderr
retorno42
fuente
1
Decirles a los desarrolladores que no usen programas solo por razones de rendimiento es una optimización prematura. Los enfoques elegantes y fáciles de seguir deberían preferirse al código difícil de entender que funciona mejor (en el orden de milisegundos).
GuyPaddock
@GuyPaddock lo siento, no has leído esto correctamente. Abetos; Se trata de redirigir tuberías que se manejan bien con bash. Si a uno no le gusta la sintaxis (fea) de cómo redirige bash, debe dejar de implementar scripts bash o aprender la forma bash. Segundo; debe saber lo costoso que es lanzar un nuevo proceso en comparación con llamar a un bash incorporado.
regreso42
1
Hay una diferencia entre informar a alguien sobre las compensaciones de rendimiento de las funciones integradas de Bash caty ordenarle a alguien que no use cat porque es lento. Hay innumerables casos de uso en los que cat es la elección correcta, por eso me opongo a su respuesta.
GuyPaddock
@GuyPaddock El abridor solicitó un echoreemplazo. Incluso si usa cat, tiene que usar un redireccionamiento bash. de todas formas. Entonces, no tiene ningún sentido usar cataquí. Por cierto, uso cat100 veces al día, pero nunca en el contexto que el abridor pidió ... ¿lo entendiste?
return42
8

Otra opción con la que me topé recientemente es esta:

    {
        echo "First error line"
        echo "Second error line"
        echo "Third error line"
    } >&2

Esto usa solo las funciones integradas de Bash mientras hace que la salida de error de varias líneas sea menos propensa a errores (ya que no tiene que acordarse de agregar &>2a cada línea).

GuyPaddock
fuente
1
No puedo creerlo, me rechazas cuando te recomiendo usar bash-redirect y en tu propia respuesta estás usando bash-redirect.
return42
1
@ return42 Voté su respuesta porque todo lo que hizo fue decirle al OP que no hay mejor respuesta que con la que comenzaron ... no es realmente una respuesta. Tampoco veo una sugerencia de sub-shell en su respuesta ... su respuesta realmente solo aconseja al OP que no use cato cualquier otra utilidad, lo cual está fuera del tema de la pregunta.
GuyPaddock el
6

read es un comando integrado de shell que imprime en stderr, y se puede usar como echo sin realizar trucos de redireccionamiento:

read -t 0.1 -p "This will be sent to stderr"

El -t 0.1es un tiempo de espera que deshabilita la funcionalidad principal de lectura, almacenando una línea de stdin en una variable.

Douglas Mayle
fuente
55
Bash en OS X no permite el "0.1"
James Roth
2

Hacer un guion

#!/bin/sh
echo $* 1>&2

Esa sería tu herramienta.

O haga una función si no desea tener un script en un archivo separado.

n0rd
fuente
66
Mejor que sea una función (como la respuesta de James Roth), y mejor transmitir todos los argumentos, no solo el primero.
Cascabel
2
¿Por qué una función sería mejor? (O, alternativamente: "Mejor explicar por qué sería mejor ...")
Ogre Psalm33
3
@ OgrePsalm33 Una razón por la cual una función sería mejor es que cuando se llama a un script, generalmente se crea una nueva instancia de shell para proporcionar un entorno en el que ejecutar el script. Una función, por otro lado, se coloca en el entorno del shell actualmente en ejecución. Llamar a una función, en este caso, sería una operación mucho más eficiente ya que se evitaría la creación de otra instancia de un shell.
destenson
0

Solución combinada sugerida por James Roth y Glenn Jackman

  • agregue el código de color ANSI para mostrar el mensaje de error en rojo:
echoerr() { printf "\e[31;1m%s\e[0m\n" "$*" >&2; }

# if somehow \e is not working on your terminal, use \u001b instead
# echoerr() { printf "\u001b[31;1m%s\u001b[0m\n" "$*" >&2; }

echoerr "This error message should be RED"
Polimerasa
fuente
-10

Mac OS X: probé la respuesta aceptada y un par de otras respuestas y todas resultaron en escribir STDOUT no STDERR en mi Mac.

Aquí hay una forma portátil de escribir en un error estándar usando Perl:

echo WARNING! | perl -ne 'print STDERR'
Noah Sussman
fuente
jajaja, ¡vota todo lo que quieras pero esta es la solución que realmente uso en mi código!
Noah Sussman