bash: escapa de líneas individuales del eco '-x'

11

En bash, cuando se ejecuta con la -xopción, ¿es posible eximir los comandos individuales del eco?

Estoy tratando de hacer que la salida sea lo más ordenada posible, por lo que estoy ejecutando ciertas partes de mi script en una subshell con set +x. Sin embargo, la fila en set +xsí todavía se repite y no agrega información valiosa a la salida.

Recuerdo que en los viejos .battiempos, cuando corría con echo on, las líneas individuales podían estar exentas al comenzar con un @. ¿Hay algún equivalente en bash?

#!/bin/bash -x

function i_know_what_this_does() {
  (
    set +x
    echo do stuff
  )
}

echo the next-next line still echoes 'set +x', is that avoidable?
i_know_what_this_does
echo and we are back and echoing is back on

Al ejecutar lo anterior, la salida es:

+ echo the next-next line still echoes 'set +x,' is that 'avoidable?'
the next-next line still echoes set +x, is that avoidable?
+ i_know_what_this_does
+ set +x
do stuff
+ echo and we are back and echoing is back on
and we are back and echoing is back on
clacke
fuente

Respuestas:

22

xtracela salida va a stderr, por lo que puede redirigir stderra /dev/null:

i_know_what_this_does() {
  echo do stuff
} 2> /dev/null

Si aún desea ver los errores de los comandos que se ejecutan dentro de las funciones, puede hacer

i_know_what_this_does() (
  { set +x; } 2> /dev/null # silently disable xtrace
  echo do stuff
)

Tenga en cuenta el uso de en (...)lugar de {...}proporcionar un ámbito local para esa función a través de una subshell. bash, ya que la versión 4.4 ahora admite local -como en el shell Almquist para hacer que las opciones sean locales a la función (similar a set -o localoptionsin zsh), por lo que puede evitar el subshell haciendo:

i_know_what_this_does() {
  { local -; set +x; } 2> /dev/null # silently disable xtrace
  echo do stuff
}

Una alternativa para bash4.0 a 4.3 sería usar la $BASH_XTRACEFDvariable y tener abierto un descriptor de archivo dedicado /dev/nullpara eso:

exec 9> /dev/null
set -x
i_know_what_this_does() {
  { local BASH_XTRACEFD=9; } 2> /dev/null # silently disable xtrace
  echo do stuff
}

Sin embargo, dado que bashcarece de la capacidad de marcar un fd con el indicador close-on-exec , eso tiene el efecto secundario de filtrar ese fd a otros comandos.

Vea también este locvar.sh que contiene algunas funciones para implementar el alcance local para variables y funciones en scripts POSIX y también proporciona trace_fny untrace_fnfunciones para hacerlos xtrace d o no.

Stéphane Chazelas
fuente
¡Dulce! Estaba buscando para ver si había algún modificador que pudiera aplicar a la función en sí, pero no pensé en simplemente redirigir stderr. ¡Gracias!
clacke
1
Por cierto , stchaz.free.fr/which_interpreter de la misma página es bastante sorprendente e inquietante. :-)
clacke
Y ahora volví aquí nuevamente para el segundo método, silenciando set + x sin silenciar la salida stderr útil. ¡Gracias de nuevo!
clacke
2

La razón por la que set +xse imprime es que set -xsignifica "imprima el comando que está a punto de ejecutar, con expansiones, antes de ejecutarlo . Por lo tanto, el shell no sabe que desea que no imprima cosas hasta que haya impreso la línea diciéndole que no imprimir cosas. Hasta donde sé, no hay forma de evitar que eso suceda.

Jenny D
fuente
0

Aquí está la solución que has estado buscando:

function xtrace() {
  # Print the line as if xtrace was turned on, using perl to filter out
  # the extra colon character and the following "set +x" line.
  (
    set -x
    # Colon is a no-op in bash, so nothing will execute.
    : "$@"
    set +x
  ) 2>&1 | perl -ne 's/^[+] :/+/ and print' 1>&2
  # Execute the original line unmolested
  "$@"
}

El comando original se ejecuta en el mismo shell bajo una transformación de identidad. Justo antes de ejecutar, obtienes una xtrace no recursiva de los argumentos. Esto le permite rastrear los comandos que le interesan sin enviar spam a stederr con copias duplicadas de cada comando "echo".

# Example
echo "About to do something complicated ..."
xtrace do_something_complicated
Dave Dopson
fuente
O para evitar perl(y problemas con los comandos de varias líneas):+() { :;} 2> /dev/null; xtrace() { (PS4=; set -x; + "$@";{ set +x; } 2> /dev/null); "$@";}
Stéphane Chazelas
No, lo siento, unix.stackexchange.com/a/60049/17980 es la solución que estaba buscando. :-) ¿Las set -xmaniobras me compran algo en comparación con solo printf >&2 '+ %s\n' "$*"?
clacke
Como en:xtrace() { printf >&2 '+ %s\n' "$*"; "$@"; }
clacke