Cuando estás cerca de tu cuello en caimanes, es fácil olvidar que el objetivo era drenar el pantano.
- dicho popular
La pregunta es sobre echo
, y sin embargo, la mayoría de las respuestas hasta ahora se han centrado en cómo introducir un set +x
comando. Hay una solución mucho más simple y directa:
{ echo "Message"; } 2> /dev/null
(Reconozco que podría no haber pensado en { …; } 2> /dev/null
eso si no lo hubiera visto en las respuestas anteriores).
Esto es algo engorroso, pero, si tiene un bloque de echo
comandos consecutivos , no necesita hacerlo en cada uno individualmente:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
Tenga en cuenta que no necesita punto y coma cuando tiene líneas nuevas.
Puede reducir la carga de escritura utilizando la idea
de kenorb de abrir /dev/null
permanentemente en un descriptor de archivo no estándar (por ejemplo, 3) y luego decir en 2>&3
lugar de 2> /dev/null
todo el tiempo.
Las primeras cuatro respuestas al momento de escribir esto requieren hacer algo especial (y, en la mayoría de los casos, engorroso)
cada vez que haces una echo
. Si realmente desea que todos los echo
comandos supriman la traza de ejecución (¿y por qué no lo haría?), Puede hacerlo globalmente, sin mezclar mucho código. Primero, noté que no se rastrean los alias:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(Tenga en cuenta que los siguientes fragmentos de script funcionan si shebang es #!/bin/sh
, incluso si /bin/sh
es un enlace a bash. Pero, si es shebang #!/bin/bash
, debe agregar un shopt -s expand_aliases
comando para que los alias funcionen en un script).
Entonces, para mi primer truco:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
Ahora, cuando decimos echo "Message"
, estamos llamando al alias, que no se rastrea. El alias desactiva la opción de rastreo, mientras suprime el mensaje de rastreo del set
comando (utilizando la técnica presentada primero en la respuesta del usuario 5071535 ) y luego ejecuta el echo
comando real . Esto nos permite obtener un efecto similar al de la respuesta del usuario 5071535 sin necesidad de editar el código en cada echo
comando. Sin embargo, esto deja el modo de rastreo desactivado. No podemos poner un set -x
en el alias (o al menos no fácilmente) porque un alias solo permite que una cadena sea sustituida por una palabra; ninguna parte de la cadena de alias se puede inyectar en el comando
después de los argumentos (por ejemplo, "Message"
). Entonces, por ejemplo, si el script contiene
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
la salida sería
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
así que aún necesita volver a activar la opción de rastreo después de mostrar los mensajes, pero solo una vez después de cada bloque de echo
comandos consecutivos :
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Sería bueno si pudiéramos hacer la set -x
automática después de un echo
- y podemos hacerlo , con un poco más de trucos. Pero antes de presentar eso, considere esto. El OP comienza con scripts que usan #!/bin/sh -ex
shebang. Implícitamente, el usuario podría eliminar el x
archivo shebang y tener un script que funcione normalmente, sin rastreo de ejecución. Sería bueno si pudiéramos desarrollar una solución que conserve esa propiedad. Las primeras respuestas aquí fallan esa propiedad porque activan el rastreo "de regreso" después de las echo
declaraciones, incondicionalmente, sin tener en cuenta si ya estaba activado.
Esta respuesta no reconoce ese problema, ya que reemplaza echo
salida con salida de rastreo; por lo tanto, todos los mensajes desaparecen si el rastreo está desactivado. Ahora presentaré una solución que activa el rastreo después de una echo
declaración condicionalmente , solo si ya estaba activada. Reducir esto a una solución que activa el rastreo "incondicionalmente" es trivial y se deja como un ejercicio.
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
es la lista de opciones; una concatenación de las letras correspondientes a todas las opciones que se configuran. Por ejemplo, si las opciones e
y x
están configuradas, $-
habrá un revoltijo de letras que incluye e
y x
. Mi nuevo alias (arriba) guarda el valor de $-
antes de desactivar el rastreo. Luego, con el rastreo desactivado, arroja el control a una función de shell. Esa función hace lo real echo
y luego verifica si la x
opción estaba activada cuando se invocó el alias. Si la opción estaba activada, la función la vuelve a activar; si estaba apagado, la función lo deja.
Puede insertar las siete líneas anteriores (ocho, si incluye una shopt
) al comienzo del guión y dejar el resto solo.
Esto te permitiría
- para usar cualquiera de las siguientes líneas shebang:
#! / bin / sh -ex
#! / bin / sh -e
#! / bin / sh –x
o simplemente#! / bin / sh
y debería funcionar como se esperaba.
- tener un código como
(shebang)
comando 1
comando 2
comando 3
set -x
comando 4
comando 5
comando 6
conjunto + x
comando 7
comando 8
comando 9
y
- Los comandos 4, 5 y 6 se rastrearán, a menos que uno de ellos sea un
echo
, en cuyo caso se ejecutará pero no se rastreará. (Pero incluso si el comando 5 es un echo
, el comando 6 aún se rastreará).
- Los comandos 7, 8 y 9 no se rastrearán. Incluso si el comando 8 es un
echo
, el comando 9 todavía no se rastreará.
- Los comandos 1, 2 y 3 se rastrearán (como 4, 5 y 6) o no (como 7, 8 y 9) dependiendo de si el shebang incluye
x
.
PD: He descubierto que, en mi sistema, puedo omitir la builtin
palabra clave en mi respuesta intermedia (la que es solo un alias echo
). Esto no es sorprendente; bash (1) dice que, durante la expansión de alias, ...
... una palabra que es idéntica a un alias que se expande no se expande por segunda vez. Esto significa que uno puede alias ls
de ls -F
, por ejemplo, y bash no intenta expandir recursivamente el texto de reemplazo.
No es sorprendente que la última respuesta (la que tiene echo_and_restore
) falle si builtin
se omite la palabra clave 1 . Pero, curiosamente, funciona si borro builtin
y cambio el orden:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 Parece dar lugar a un comportamiento indefinido. He visto
- un bucle infinito (probablemente debido a una recursión ilimitada),
- un
/dev/null: Bad address
mensaje de error y
- un basurero
echo +x; echo "$*"; echo -x
un alias, me gustaría verlo.Encontré una solución parcial en InformIT :
salidas
Desafortunadamente, eso todavía resuena
set +x
, pero al menos está tranquilo después de eso. entonces es al menos una solución parcial al problema.¿Pero hay quizás una mejor manera de hacer esto? :)
fuente
De esta manera mejora su propia solución al deshacerse de la
set +x
salida:fuente
Poner
set +x
dentro de los corchetes, por lo que se aplicaría solo para el ámbito local.Por ejemplo:
daría salida:
fuente
set +x
interior del subshell (o el uso de subshells en absoluto) esté haciendo algo útil. Puede eliminarlo y obtener el mismo resultado. Es el redireccionador stderr a / dev / null el que está haciendo el trabajo de "deshabilitar" el rastreo temporalmente ... Parece queecho foo1 2>/dev/null
, etc., sería igual de efectivo y más legible.bash -x
) y ya está redirigiendo&2
a nulo (ya que&3
fue redirigido a nulo), por lo que no estoy seguro de cómo ese comentario es relevante. Tal vez solo necesitemos un mejor ejemplo que ilustre su punto, pero en el ejemplo dado al menos, todavía parece que podría simplificarse sin perder ningún beneficio.Me encanta la respuesta exhaustiva y bien explicada de g-man , y considero que es la mejor que se ha proporcionado hasta ahora. Se preocupa por el contexto del script y no fuerza las configuraciones cuando no son necesarias. Entonces, si está leyendo esta respuesta primero, continúe y verifique esa, todo el mérito está ahí.
Sin embargo, en esa respuesta falta una pieza importante: el método propuesto no funcionará para un caso de uso típico, es decir, informar errores:
Debido a cómo se construye el alias, esto se expandirá a
y lo adivinaste,
echo_and_restore
se ejecuta siempre , incondicionalmente. Dado que laset +x
parte no se ejecutó, significa que el contenido de esa función también se imprimirá.Cambiar el último
;
a&&
tampoco funcionaría, porque en Bash,||
y&&
son asociativos a la izquierda .Encontré una modificación que funciona para este caso de uso:
Utiliza una subshell (la
(...)
parte) para agrupar todos los comandos, y luego pasa la cadena de entrada a través de stdin como Here String (la<<<
cosa) que luego imprimecat -
. El-
es opcional, pero ya sabes, " explícito es mejor que implícito ".También podría usar
cat -
directamente sin el eco , pero me gusta de esta manera porque me permite agregar cualquier otra cadena a la salida. Por ejemplo, tiendo a usarlo así:Y ahora funciona de maravilla:
fuente
El seguimiento de ejecución va a
stderr
, filtre de esta manera:Alguna explicación, paso a paso:
stderr
se redirige ... -2>
>(…)
grep
es el comando ...^
+ echo
...grep
invierte el partido ... --v
stdout
; lo redirigimos astderr
donde pertenece. ->&2
El problema es (supongo) que esta solución puede desincronizar las transmisiones. Debido al filtrado
stderr
puede ser un poco tarde en relación constdout
(donde laecho
salida pertenece por defecto). Para solucionarlo, puede unirse a las transmisiones primero si no le importa tener ambas enstdout
:Puede crear un filtro de este tipo en el script en sí, pero este enfoque es propenso a la desincronización con seguridad (es decir, ha ocurrido en mis pruebas: el seguimiento de la ejecución de un comando puede aparecer después de la salida de seguimiento inmediato
echo
).El código se ve así:
Ejecútalo sin ningún truco:
Nuevamente, use
> >(grep -v "^+ echo ") 2>&1
para mantener la sincronización a costa de unirse a las transmisiones.Otro enfoque. Obtiene una salida "un poco redundante" y de aspecto extraño porque su terminal se mezcla
stdout
ystderr
. Estas dos corrientes son animales diferentes por una razón. Compruebe si analizarstderr
solo se ajusta a sus necesidades; descartarstdout
:Si tiene en su script un
echo
mensaje de error / depuración de impresión,stderr
entonces puede deshacerse de la redundancia de la manera descrita anteriormente. Comando completo:Esta vez solo estamos trabajando
stderr
, por lo que la desincronización ya no es una preocupación. Desafortunadamente, de esta manera no verá un rastro ni salida de lasecho
impresionesstdout
(si las hay). Podríamos tratar de reconstruir nuestro filtro para detectar la redirección (>&2
), pero si nos fijamos enecho foobar >&2
,echo >&2 foobar
yecho "foobar >&2"
entonces probablemente estarán de acuerdo que las cosas se complican.Mucho depende de los ecos que tenga en sus guiones. Piénselo dos veces antes de implementar un filtro complejo, puede ser contraproducente. Es mejor tener un poco de redundancia que perder accidentalmente alguna información crucial.
En lugar de descartar la traza de ejecución de un
echo
, podemos descartar su salida, y cualquier salida, excepto las trazas. Para analizar solo las trazas de ejecución, intente:¿Infalible? No. Piensa qué sucederá si hay
echo "+ rm -rf --no-preserve-root /" >&2
en el guión. Alguien podría sufrir un ataque al corazón.Y finalmente…
Afortunadamente hay
BASH_XTRACEFD
una variable ambiental. Deman bash
:Podemos usarlo así:
Tenga en cuenta que la primera línea genera una subshell. De esta forma, el descriptor de archivo no seguirá siendo válido ni la variable asignada en el shell actual después.
Gracias a
BASH_XTRACEFD
usted puede analizar los rastros libres de ecos y cualquier otra salida, sean cuales sean. No es exactamente lo que querías, pero mi análisis me hace pensar que es (en general) la forma correcta.Por supuesto, puede usar otro método, especialmente cuando necesita analizar
stdout
y / ostderr
junto con sus trazas. Solo necesita recordar que existen ciertas limitaciones y dificultades. Traté de mostrar (algunos) de ellos.fuente
En un Makefile puedes usar el
@
símbolo.Ejemplo de uso:
@echo 'message'
De este documento de GNU:
fuente
./test.sh: line 1: @echo: command not found
, pero estoy usando bash.@
solo funciona cuando haces eco en archivos MAKE.Editado el 29 de octubre de 2016 por moderador sugiere que el original no contenía suficiente información para comprender lo que está sucediendo.
Este "truco" produce solo una línea de salida de mensaje en el terminal cuando xtrace está activo:
La pregunta original es ¿Hay alguna manera de dejar -x habilitado, pero solo muestra el mensaje en lugar de las dos líneas ? Esta es una cita exacta de la pregunta.
Entiendo la pregunta, ¿cómo "dejar set -x habilitado Y producir una sola línea para un mensaje"?
En resumen, el OP requiere:
El OP no requiere que el se use el comando echo . Lo citaron como un ejemplo de producción de mensajes, utilizando la abreviatura, por ejemplo, que significa latín exempli gratia, o "por ejemplo".
Al pensar "fuera de la caja" y abandonar el uso del eco para producir mensajes, noto que una declaración de asignación puede cumplir con todos los requisitos.
Haga una asignación de una cadena de texto (que contiene el mensaje) a una variable inactiva.
Agregar esta línea a un script no altera ninguna lógica, pero produce una sola línea cuando el rastreo está activo: (Si está utilizando la variable $ echo , simplemente cambie el nombre a otra variable no utilizada)
Lo que verá en la terminal es solo 1 línea:
no dos, como no le gusta al OP:
Aquí hay un script de muestra para demostrar. Tenga en cuenta que la sustitución de variables en un mensaje (el nombre del directorio $ HOME a 4 líneas del final) funciona, por lo que puede rastrear variables utilizando este método.
Y aquí está el resultado de ejecutarlo en la raíz, luego el directorio de inicio.
fuente
echo "Here are the files in this directory:" *
?