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.