¿Suprimir el seguimiento de ejecución del comando echo?

¿Suprimir el seguimiento de ejecución del comando echo?

Estoy ejecutando scripts de shell de Jenkins, que inician los scripts de shell con las opciones shebang #!/bin/sh -ex.

De acuerdo a¿Bash Shebang para tontos?, -x"hace que el shell imprima un seguimiento de ejecución", lo cual es excelente para la mayoría de los propósitos, excepto para los ecos:

echo "Message"

produce la salida

+ echo "Message"
Message

lo cual es un poco redundante y parece un poco extraño. ¿Hay alguna manera de dejarlo -xhabilitado, pero solo la salida?

Message

en lugar de las dos líneas anteriores, por ejemplo, anteponiendo al comando echo un carácter de comando especial o redirigiendo la salida.

Respuesta1

Cuando estás lleno de caimanes hasta el cuello, es fácil olvidar que el objetivo era drenar el pantano.                    - dicho popular

La pregunta es sobreechoy, sin embargo, la mayoría de las respuestas hasta ahora se han centrado en cómo set +xintroducir un comando. Hay una solución mucho más simple y directa:

{ echo "Message"; } 2> /dev/null

(Reconozco que tal vez no hubiera pensado en { …; } 2> /dev/null si no lo hubiera visto en las respuestas anteriores).

Esto es algo engorroso, pero, si tienes un bloque de echocomandos consecutivos, no necesitas 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 nuevas líneas.

Puede reducir la carga de escritura usandola idea de kenorb de abrir /dev/nullpermanentemente en un descriptor de archivo no estándar (por ejemplo, 3) y luego decir 2>&3en lugar de 2> /dev/nulltodo el tiempo.


Las primeras cuatro respuestas al momento de escribir este artículo requieren hacer algo especial (y, en la mayoría de los casos, engorroso) cadavez que haces un echo. si realmente quierestodo echocomandos para suprimir el seguimiento de ejecución (¿y por qué no lo harías tú?), puedes hacerlo globalmente, sin necesidad de usar 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 el shebang es #!/bin/sh, incluso si /bin/shes un enlace a bash. Pero, si el shebang es #!/bin/bash, debe agregar un shopt -s expand_aliasescomando 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", llamamos al alias, que no se rastrea. El alias desactiva la opción de seguimiento, mientras suprime el mensaje de seguimiento del setcomando (utilizando la técnica presentada primero enRespuesta del usuario5071535), y luego ejecuta el echocomando real. Esto nos permite obtener un efecto similar al de la respuesta del usuario5071535 sin necesidad de editar el código encada echodominio. Sin embargo, esto deja el modo de rastreo desactivado. No podemos poner a set -xen el alias (o al menos no fácilmente) porque un alias sólo permite sustituir una palabra por una cadena; ninguna parte de la cadena de alias se puede inyectar en el comando despuéslos 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

por lo que aún deberá volver a activar la opción de rastreo después de mostrar mensajes, pero solo una vez después de cadabloquearde comandos consecutivos echo:

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date


Sería buenosi pudiéramos hacer el set -xautomático después de un echo... y podemos, con un poco más de astucia. Pero antes de presentar eso, considere esto. El OP comienza con scripts que usan un #!/bin/sh -exshebang. Implícitamente, el usuario podría eliminarlo xdel shebang y tener un script que funcione normalmente, sin seguimiento de ejecución. Sería bueno si pudiéramos desarrollar una solución que conservara esa propiedad. Las primeras respuestas aquí fallan en esa propiedad porque activan el rastreo "hacia atrás" después de echolas declaraciones, incondicionalmente, sin tener en cuenta si ya estaba activado.  esta respuestanotoriamente no reconoce esa cuestión, ya quereemplaza echosalida con salida de seguimiento; por lo tanto, todos los mensajes desaparecen si se desactiva el rastreo. Ahora presentaré una solución que vuelve a activar el rastreo después de una echodeclaración.condicionalmente– solo si ya estaba encendido. Degradar esto a una solución que active el rastreo “hacia atrás” incondicionalmente es trivial y se deja como 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 se configuran las opciones ey , habrá un revoltijo de letras que incluye y . Mi nuevo alias (arriba) guarda el valor de antes de desactivar el seguimiento. Luego, con el rastreo desactivado, transfiere el control a una función de shell. Esa función hace lo real y luego verifica si la 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 apagado.x$-ex$-echox

Puede insertar las siete líneas anteriores (ocho, si incluye un shopt) al comienzo del guión y dejar el resto como está.

Esto te permitiría

  1. para utilizar 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.
  2. tener un código como
    (el asunto)
    comando 1
    comando 2
    comando 3
    conjunto -x
    comando 4
    comando 5
    comando 6
    conjunto +x
    comando 7
    comando 8
    comando 9
    y
    • Se rastrearán los comandos 4, 5 y 6, 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 echocomando 6, aún así se rastreará).
    • No se rastrearán los comandos 7, 8 y 9. Incluso si el comando 8 es un comando echo9, el comando 9 aún no será rastreado.
    • 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 builtinpalabra clave en mi respuesta del medio (la que es solo un alias para echo). Esto no es sorprendente; bash(1) dice que, durante la expansión del alias,…

… una palabra que es idéntica a un alias que se está expandiendo no se expande por segunda vez. Esto significa que uno puede aliaslsals -F, por ejemplo, y bash no intenta expandir recursivamente el texto de reemplazo.

No es de extrañar que la última respuesta (la que tiene echo_and_restore) falle si builtinse omite la palabra clave 1 . Pero, curiosamente, funciona si elimino builtiny 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 la recursividad ilimitada),
  • un /dev/null: Bad addressmensaje de error, y
  • un volcado de memoria.

Respuesta2

Encontré una solución parcial enInformar:

#!/bin/bash -ex
set +x; 
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"

salidas

set +x; 
shell tracing is disabled here 
+ echo "but is enabled here"
but is enabled here

Desafortunadamente, eso todavía resuena set +x, pero al menos después de eso hay silencio. por lo que es al menos una solución parcial al problema.

Pero, ¿existe tal vez una mejor manera de hacer esto? :)

Respuesta3

De esta manera mejora su propia solución al eliminar el set +xresultado:

#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"

Respuesta4

Colóquelo set +xentre corchetes, de modo que se aplique solo al ámbito local.

Por ejemplo:

#!/bin/bash -x
exec 3<> /dev/null
(echo foo1 $(set +x)) 2>&3
($(set +x) echo foo2) 2>&3
( set +x; echo foo3 ) 2>&3
true

saldría:

$ ./foo.sh 
+ exec
foo1
foo2
foo3
+ true

información relacionada