Pomiń ślad wykonania bash (zestaw -x) z zewnątrz skryptu

17

Próbowałem znaleźć odpowiedź na to pytanie, ale jak dotąd nie miałem szczęścia:

Mam skrypt, który uruchamia inne skrypty, a wiele innych skryptów zawiera „set -x”, co powoduje, że wypisują każde wykonane polecenie. Chciałbym się tego pozbyć, ale zachowuję informacje, jeśli którykolwiek ze skryptów wyśle ​​komunikat o błędzie do stderr.

Więc nie mogę po prostu pisać ./script 2>/dev/null

Ponadto nie mam uprawnień do edytowania tych innych skryptów, więc nie mogę ręcznie zmienić opcji set.

Myślałem o zarejestrowaniu wszystkiego, od stderr do osobnego pliku i odfiltrowaniu poleceń śledzenia, ale może istnieje prostszy sposób?

człowiek
źródło
1
./script 2>some_file
Satō Katsura,

Odpowiedzi:

25

W wersji bash4.1 i nowszej możesz to zrobić

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

(działa również, gdy bashzostanie wywołany jako sh).

Zasadniczo mówimy, bashaby wyświetlać dane xtracewyjściowe na deskryptorze pliku 7 zamiast wartości domyślnej 2 i przekierować ten deskryptor pliku na /dev/null. Numer fd jest dowolny. Użyj fd powyżej 2, który nie jest inaczej używany w twoim skrypcie. Jeśli powłoka, w której wpisujesz to polecenie, to bashlub yash, możesz nawet użyć liczby powyżej 9 (chociaż możesz napotkać problemy, jeśli deskryptor pliku jest używany wewnętrznie przez powłokę).

Jeśli powłoka, z której wywołujesz ten bashskrypt zsh, to możesz także:

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

aby zmienna była automatycznie przypisywana pierwszej wolnej wartości fd powyżej 9.

W starszych wersjach bashinną opcją, jeśli xtracezostanie włączony za pomocą set -x(w przeciwieństwie do #! /bin/bash -xlub set -o xtrace) byłoby przedefiniowanie setjako wyeksportowanej funkcji, która nic nie robi po przekazaniu -x(choć to zepsułoby skrypt, jeśli on (lub inny bashskrypt, który wywołuje) służy setdo ustawiania parametrów pozycyjnych).

Lubić:

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

Inną opcją jest dodanie pułapki DEBUG do $BASH_ENVpliku, który działa set +xprzed każdym poleceniem.

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

To nie zadziała, gdy set -xzostanie wykonane w podpowłoce.

Jak powiedział @ilkkachu, pod warunkiem, że masz uprawnienia do zapisu w dowolnym folderze w systemie plików, powinieneś być przynajmniej w stanie wykonać kopię skryptu i edytować go.

Jeśli nie ma gdzie napisać kopii skryptu, lub jeśli nie jest wygodne tworzenie i edytowanie nowej kopii za każdym razem, gdy pojawi się aktualizacja oryginalnego skryptu, nadal możesz zrobić:

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

To (i podejście do kopiowania) może nie działać poprawnie, jeśli skrypt robi coś wymyślnego $0lub specjalne zmienne, takie jak $BASH_SOURCE(np. Szukanie plików, które są względne względem położenia samego skryptu), więc może być konieczne wykonanie dalszej edycji, takiej jak zastąp $0ścieżką skryptu ...

Stéphane Chazelas
źródło
Twoja pierwsza odpowiedź jest dokładnie tym, czego potrzebowałem, czystym i eleganckim. Czy mógłbyś wyjaśnić trochę, jak to działa? Dlaczego liczba 7? Do czego można użyć innych liczb? Dzięki.
człowiek
@human, patrz edycja
Stéphane Chazelas
1
{BASH_XTRACEFD}>Sztuczka działa w bash4.1 lub nowszy, jak również.
chepner
@chepner, tak, funkcja została dodana do zsh, ksh93 i bash w tym samym czasie, zgodnie z sugestią dewelopera zsh. Ale tutaj to nie działa ksh93albo bashże zmienna nie jest przekazywana w środowisku polecenia (porównaj <shell> -c 'export fd; printenv fd {fd}> /dev/null'w zsh, bashi ksh93). Możesz sprawić, by działał w ksh93/ bashwykonując go w dwóch krokach lub ewentualnie używając eval, ale dla bash, miałoby to skutki uboczne, gdyby włączona była opcja xtrace.
Stéphane Chazelas,
5

Ponieważ oni skrypty, to mógłby zrobić ich kopie i edytować te.

Poza tym filtrowanie danych wyjściowych wydaje się proste, możesz jawnie ustawić PS4coś bardziej niezwykłego niż pojedynczy plus, aby ułatwić filtrowanie:

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

(oczywiście, że to załamie się na stdout i stdin, ale dodawanie stderr w Bash robi się trochę owłosione, więc zignoruję to)

ilkkachu
źródło
1
Rurociągi tylko stderr jest proste: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick,
4
@Patrick, wykonanie tego w bashma problemy, ponieważ grepjest uruchamiane asynchronicznie (bash nie czeka na to, więc może (i często robi) dane wyjściowe po uruchomieniu następnego polecenia w skrypcie).
Stéphane Chazelas