Bash ustawił + x bez drukowania

96

Czy ktoś wie, czy możemy powiedzieć set +xw bashu bez drukowania:

set -x
command
set +x

ślady

+ command
+ set +x

ale powinien po prostu wydrukować

+ command

Bash to wersja 4.1.10 (4). Od jakiegoś czasu mnie to niepokoi - wyjście jest zaśmiecone bezużytecznymi set +xwierszami, przez co narzędzie śledzenia nie jest tak przydatne, jak mogłoby być.

Andreas Spindler
źródło
To nie jest odpowiedzią na twoje pytanie, ale kiedy uruchamiasz skrypt, dlaczego nie:script.sh 2>&1 | grep -v 'set +x'
cdarke

Odpowiedzi:

149

Miałem ten sam problem i udało mi się znaleźć rozwiązanie, które nie używa podpowłoki:

set -x
command
{ set +x; } 2>/dev/null
McJoey
źródło
10
Świetna odpowiedź, tylko uwaga: bez średnika po poleceniu to nie zadziała; ze średnikiem, ale bez spacji w nawiasach klamrowych, zostanie zgłoszony błąd składni.
sdaau
9
To zeruje status wyjścia.
Garth Kidd,
8
Status wyjścia @GarthKidd jest zerowany po każdym pomyślnym poleceniu. set +xto takie udane polecenie
Daniel Alder,
4
W przypadkach, w których potrzebny jest kod zakończenia command, ta odmiana jest rozwiązanie: { STATUS=$?; set +x; } 2>/dev/null. Następnie $STATUSw wolnym czasie sprawdź kolejne linie.
Greg Price,
5
Oddzielnie, oto wersja golfed nieco dalej: { set +x; } 2>&-. To całkowicie zamyka fd 2, zamiast wskazywać na / dev / null. Niektóre programy nie radzą sobie tak dobrze, gdy próbują drukować na stderr, dlatego / dev / null jest ogólnie dobrym stylem; ale set -xśledzenie muszli radzi sobie z tym dobrze, więc działa tutaj idealnie i sprawia, że ​​inkantacja jest nieco krótsza.
Greg Price
43

Możesz użyć podpowłoki. Po wyjściu z podpowłoki ustawienie xzostanie utracone:

( set -x ; command )
choroba
źródło
Cóż, dzięki ... słuszna uwaga. Właściwie jestem świadomy „sztuczki podpowłoki”. Miałem nadzieję, że to może być prostsze. Obejmuje istotne zmiany w kodzie, czyniąc go złożonym i mniej czytelnym. IMHO, to byłoby gorsze niż życie z zestawem + x linii ...
Andreas Spindler
Nie widzę ( set -x \n command \n )nic gorszego niż set -x \n command \n set +x.
chepner
3
@chepner: Nie możesz ustawiać zmiennych.
choroba
2
... i nie możesz cd: nie zmienia bieżącego katalogu w powłoce nadrzędnej.
Andreas Spindler
Przepraszam, nie jestem pewien, co myślałem, że faktycznie był to twój sprzeciw. To był długi tydzień ...
chepner
8

Niedawno zhakowałem rozwiązanie tego problemu, kiedy to mnie zdenerwowało:

shopt -s expand_aliases
_xtrace() {
    case $1 in
        on) set -x ;;
        off) set +x ;;
    esac
}
alias xtrace='{ _xtrace $(cat); } 2>/dev/null <<<'

Pozwala to włączać i wyłączać xtrace, jak poniżej, gdzie loguję, jak argumenty są przypisywane do zmiennych:

xtrace on
ARG1=$1
ARG2=$2
xtrace off

Otrzymasz wynik, który wygląda następująco:

$ ./script.sh one two
+ ARG1=one
+ ARG2=two
user108471
źródło
Sprytna sztuczka (chociaż nie potrzebujesz /dev/stdinczęści). Zastrzeżenie polega na tym, że włączenie rozszerzania aliasów w skryptach może mieć niepożądane skutki uboczne.
mklement0
Masz rację. Zredagowałem odpowiedź, aby usunąć zbędne /dev/stdin. Nie znam żadnych konkretnych skutków ubocznych, ponieważ nieinteraktywne środowisko nie powinno ładować żadnych plików, które definiują aliasy. Jakie mogą wystąpić skutki uboczne?
user108471,
1
To dobra uwaga - zapomniałem, że aliasy nie są dziedziczone, więc ryzyko jest znacznie mniejsze niż myślałem (hipotetycznie, twój skrypt może pobierać kod innej firmy, który zdarza się definiować aliasy, ale zgadzam się, że prawdopodobnie nie jest to prawdziwy światowy koncern). +1
mklement0
1
@AndreasSpindler Czy mógłbyś wyjaśnić, dlaczego uważasz, że ta technika powoduje większe prawdopodobieństwo ujawnienia bardziej poufnych informacji?
user108471
1
... zasadniczo dlatego, że aliasy i funkcje są konstrukcjami wysokiego poziomu. set +xo wiele trudniej (jeśli nie niemożliwe) jest pójść na kompromis. Ale to wciąż dobre i proste rozwiązanie - chyba najlepsze do tej pory.
Andreas Spindler
7

Co powiesz na rozwiązanie oparte na uproszczonej wersji @ user108471:

shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ set +x; } 2>/dev/null'

trace_on
...stuff...
trace_off
Oliver
źródło
Co powiesz na to? function () { set_plus_x='{ set +x; } 2>/dev/null' }
LexH,
1

Jest to kombinacja kilku pomysłów, które mogą obejmować blok kodu i zachować status wyjścia.

#!/bin/bash
shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ PREV_STATUS=$? ; set +x; } 2>/dev/null; (exit $PREV_STATUS)'

trace_on
echo hello
trace_off
echo "status: $?"

trace_on
(exit 56)
trace_off
echo "status: $?"

Po wykonaniu:

$ ./test.sh 
+ echo hello
hello
status: 0
+ exit 56
status: 56
user3286792
źródło
Nicea wysiłek, ale myślę, że ()wokół exitnie jest to konieczne. Ok. Może to jest paranoikiem, ale jeśli ten kod jest używany w ogóle masz dobry wektora ataku: zmiana definicji trace_onoraz trace_offi wstrzyknąć kod, który wczytuje wykonywanych poleceń. Jeśli używasz tylko takich „narzędzi”, jest to pouczające, ale jeśli kod jest używany z innymi, musisz rozważyć, czy zalety takich niestandardowych funkcji przeważają nad wadami. Osobiście się z { set +x; } 2>/dev/nulltym zdecydowałem, ponieważ ta konstrukcja jest powszechnie rozumiana i również nie zmienia statusu wyjścia.
Andreas Spindler
Dzięki. Podzieliłem się podejściem z moimi kolegami i oni uwielbiają to, jak sprawia, że ​​zarówno wyjście, jak i kod są mniej hałaśliwe. Odnośnie ()całego exit 56; tylko po to, abym mógł wykazać, że status wyjścia podprocesu był dostępny w skrypcie bash; bez parenów nie stałby się podprocesem, a skrypt zakończyłby pracę w tym momencie ze statusem = 65.
user3286792