Suprimir la traza de ejecución de bash (set -x) desde el exterior del guión

17

He intentado encontrar una respuesta a esta pregunta, pero no tengo suerte hasta ahora:

Tengo un script que se ejecuta algunos otros scripts, y muchos de esos otros scripts tienen "conjunto -x" en ellos, lo que hace que se imprimen todos los comandos se ejecutan. Me gustaría deshacerse de eso, pero retener la información si alguna de las secuencias de comandos enviar el mensaje de error en stderr.

Así que no puedo escribir simplemente ./script 2>/dev/null

Además, no tengo privilegios para editar los otros scripts, así que no puedo cambiar manualmente la opción set.

Estaba pensando en el registro de todo, desde stderr al archivo separado y filtrado de los comandos de rastreo, pero tal vez hay una manera más sencilla?

humano
fuente
1
./script 2>some_file
Sato Katsura

Respuestas:

25

Con bash4.1 y superior, puedes hacer

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(también funciona cuando bashse invoca como sh).

Básicamente, le estamos diciendo basha la salida de la xtracesalida en el descriptor de archivo de 7 en lugar del predeterminado de 2, y la redirección que descriptor de archivo a /dev/null. El número fd es arbitrario. Use un fd superior a 2 que no se use de otra manera en su secuencia de comandos. Si la envoltura que está entrando en este comando es basho yash, incluso se puede utilizar un número superior a 9 (aunque es posible que encuentre problemas si el descriptor de archivo es usado internamente por la cáscara).

Si la envoltura que está llamando que bashla escritura es de zsh, también se puede hacer:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

para que la variable se asigne automáticamente el primer fd libre por encima de 9.

Para versiones anteriores de bash, otra opción, si xtracese activa con set -x(en lugar de #! /bin/bash -xo set -o xtrace) sería redefinir setcomo una función exportada que no hace nada cuando se pasa -x(aunque eso rompería el script si (o cualquier otro bashscript que invoque) utilizado setpara establecer los parámetros posicionales).

Me gusta:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

Otra opción es agregar una trampa DEPURACIÓN en un $BASH_ENVarchivo que lo hace set +xantes de cada comando.

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

Sin set -xembargo, eso no funcionará cuando se haga en un sub-shell.

Como @ilkkachu Dicho esto, siempre y cuando tenga permiso de escritura en cualquier carpeta del sistema de archivos, por lo menos debe ser capaz de hacer una copia del guión y editarlo.

Si no hay ningún lugar donde pueda escribir una copia del guión, o si no es conveniente hacer y editar una nueva copia cada vez que hay una actualización del guión original, aún puede hacer:

 bash <(sed 's/set -x/set +x/g' ./script.bash)

Eso (y el enfoque de copia) puede no funcionar correctamente si el script hace algo elegante $0o con variables especiales como $BASH_SOURCE(como buscar archivos que estén relacionados con la ubicación del script en sí), por lo que es posible que deba editar un poco más, como reemplazar $0con la ruta del script ...

Stéphane Chazelas
fuente
Su primera respuesta es exactamente lo que necesitaba, limpio y elegante. ¿Podría explicar un poco cómo funciona? ¿Por qué el número 7? ¿Para qué podrían usarse otros números? Gracias.
humano
@human, ver edición
Stéphane Chazelas
1
El {BASH_XTRACEFD}>truco también funciona en bash4.1 o posterior.
chepner
@chepner, si la característica se agregó a zsh, ksh93 y bash al mismo tiempo sobre una sugerencia de un desarrollador zsh. Sin embargo, aquí no funciona para ksh93o bashen la que la variable no se pasa en el entorno del comando (compárese <shell> -c 'export fd; printenv fd {fd}> /dev/null'en zsh, bashy ksh93). Se podría hacer que funcione en ksh93/ bashpor hacerlo en dos pasos o posiblemente usando eval, pero para bash, que tienen efectos secundarios si la opción xtrace estaba en marcha.
Stéphane Chazelas
5

Ya que son secuencias de comandos, podría hacer copias de ellos, y editar los.

Aparte de eso, filtrar la salida parecería simple, podría establecer explícitamente PS4algo más inusual que una sola ventaja, para facilitar el filtrado:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(por supuesto, eso colapsará stdout y stdin, pero las tuberías solo stderr en Bash se vuelven un poco peludas, así que lo ignoraré)

ilkkachu
fuente
1
Tubería simplemente stderr es fácil: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick
44
@Patrick, haciendo que en bashcuenta cuestiones como que grepse ejecuta de forma asíncrona (bash no está esperando por ella, por lo que puede (ya menudo lo hace) cosas salida después de que el siguiente comando en la secuencia de comandos se inicia).
Stéphane Chazelas