Sopprime la traccia dell'esecuzione bash (set -x) dall'esterno dello script


17

Ho provato a trovare una risposta a questa domanda, ma finora non ho avuto fortuna:

Ho uno script che esegue alcuni altri script e molti di quegli altri script hanno "set -x" in essi, il che li fa stampare ogni comando che eseguono. Vorrei liberarmene, ma conservare le informazioni se uno qualsiasi degli script invia il messaggio di errore a stderr.

Quindi non posso semplicemente scrivere ./script 2>/dev/null

Inoltre, non ho i privilegi per modificare quegli altri script, quindi non posso cambiare manualmente l'opzione set.

Stavo pensando di registrare tutto da stderr al file separato e filtrare i comandi di tracciamento, ma forse c'è un modo più semplice?


1
./script 2>some_file
Satō Katsura,

Risposte:


25

Con la versione bash4.1 e successive, puoi farlo

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

(funziona anche quando bashviene invocato come sh).

Fondamentalmente, stiamo dicendo bashdi produrre l' xtraceoutput sul descrittore di file 7 anziché il valore predefinito di 2 e reindirizzare il descrittore di file a /dev/null. Il numero fd è arbitrario. Usa un fd sopra 2 che non sia altrimenti usato nel tuo script. Se la shell in cui stai inserendo questo comando è basho yash, puoi persino usare un numero superiore a 9 (anche se potresti riscontrare problemi se il descrittore di file viene utilizzato internamente dalla shell).

Se la shell da cui stai chiamando lo bashscript è zsh, puoi anche fare:

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

per assegnare automaticamente alla variabile il primo fd libero sopra 9.

Per le versioni precedenti di bash, un'altra opzione, se l'opzione xtraceè attivata con set -x(al contrario di #! /bin/bash -xo set -o xtrace) sarebbe quella di ridefinire setcome una funzione esportata che non fa nulla quando viene passata -x(anche se ciò spezzerebbe lo script se (o qualsiasi altro bashscript che invoca) utilizzato setper impostare i parametri posizionali).

Piace:

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

Un'altra opzione è quella di aggiungere una trap DEBUG in un $BASH_ENVfile che fa set +xprima di ogni comando.

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

set -xTuttavia, ciò non funzionerà quando viene eseguito in una sotto-shell.

Come ha detto @ilkkachu, purché tu abbia l'autorizzazione in scrittura per qualsiasi cartella sul filesystem, dovresti almeno essere in grado di fare una copia dello script e modificarlo.

Se non c'è nessun posto in cui puoi scrivere una copia dello script, o se non è conveniente creare e modificare una nuova copia ogni volta che c'è un aggiornamento allo script originale, potresti comunque essere in grado di fare:

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

Quello (e l'approccio alla copia) potrebbe non funzionare correttamente se lo script fa qualcosa di fantasioso $0 o variabili speciali come $BASH_SOURCE(come la ricerca di file relativi alla posizione dello script stesso), quindi potrebbe essere necessario apportare ulteriori modifiche come sostituisci $0con il percorso dello script ...


La tua prima risposta è esattamente ciò di cui avevo bisogno, pulito ed elegante. Potresti spiegare un po 'come funziona? Perché il numero 7? A cosa potrebbero servire altri numeri? Grazie.
umano

@human, vedi modifica
Stéphane Chazelas

1
Il {BASH_XTRACEFD}>trucco funziona anche in bash4.1 o versioni successive.
Chepner,

@chepner, sì, la funzione è stata aggiunta a zsh, ksh93 e bash contemporaneamente su un suggerimento di uno sviluppatore zsh. Ma, qui non funziona per ksh93o bashin che la variabile non è passato nell'ambiente del comando (confrontare <shell> -c 'export fd; printenv fd {fd}> /dev/null'in zsh, bashe ksh93). Puoi farlo funzionare in ksh93/ bashfacendolo in due passaggi o eventualmente usando eval, ma per bash, avrebbe effetti collaterali se l'opzione xtrace fosse attiva.
Stéphane Chazelas,

5

Dal momento che sono script, tu potrebbe fare copie di loro, e modificare quelli.

A parte questo, filtrare l'output sembrerebbe semplice, potresti esplicitamente impostare PS4qualcosa di più insolito di un singolo plus, per rendere più semplice il filtraggio:

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

(ovviamente questo crollerà stdout e stdin, ma il piping appena stderr in Bash diventa un po 'peloso, quindi lo ignorerò)


1
Tubazioni solo stderr è facile: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick

4
@Patrick, farlo bashha problemi in quanto grepviene eseguito in modo asincrono (bash non lo sta aspettando, quindi può (e spesso fa) produrre cose dopo l'avvio del comando successivo nello script).
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.