bash: unikaj pojedynczych linii od echa `-x`

11

Czy podczas uruchamiania z -xopcją możliwe jest wyłączenie poszczególnych poleceń z echa?

Staram się, aby wynik był jak najbardziej schludny, więc uruchamiam niektóre części mojego skryptu w podpowłoce set +x. Jednak set +xsam wiersz jest nadal powtarzany i nie dodaje żadnych cennych informacji do wyniku.

Pamiętam, że w dawnych, złych .batczasach, kiedy biegałem echo on, poszczególne linie mogły być zwolnione, zaczynając je od @. Czy jest jakiś odpowiednik w bash?

#!/bin/bash -x

function i_know_what_this_does() {
  (
    set +x
    echo do stuff
  )
}

echo the next-next line still echoes 'set +x', is that avoidable?
i_know_what_this_does
echo and we are back and echoing is back on

Po uruchomieniu powyższego dane wyjściowe to:

+ echo the next-next line still echoes 'set +x,' is that 'avoidable?'
the next-next line still echoes set +x, is that avoidable?
+ i_know_what_this_does
+ set +x
do stuff
+ echo and we are back and echoing is back on
and we are back and echoing is back on
clacke
źródło

Odpowiedzi:

22

xtracewyjście przechodzi do stderr, więc możesz przekierować stderrdo /dev/null:

i_know_what_this_does() {
  echo do stuff
} 2> /dev/null

Jeśli nadal chcesz, aby błędy poleceń były uruchamiane wewnątrz funkcji, możesz to zrobić

i_know_what_this_does() (
  { set +x; } 2> /dev/null # silently disable xtrace
  echo do stuff
)

Zwróć uwagę na użycie (...)zamiast zamiast {...}do zapewnienia zasięgu lokalnego dla tej funkcji za pomocą podpowłoki. bash, ponieważ wersja 4.4 obsługuje teraz local -podobnie jak w powłoce Almquist, aby opcje były lokalne dla funkcji (podobnie do set -o localoptionsin zsh), dzięki czemu można uniknąć podpowłoki, wykonując:

i_know_what_this_does() {
  { local -; set +x; } 2> /dev/null # silently disable xtrace
  echo do stuff
}

Alternatywą dla wersji bash4.0 do 4.3 byłoby użycie $BASH_XTRACEFDzmiennej i otwarcie dedykowanego deskryptora pliku /dev/null:

exec 9> /dev/null
set -x
i_know_what_this_does() {
  { local BASH_XTRACEFD=9; } 2> /dev/null # silently disable xtrace
  echo do stuff
}

Ponieważ bashnie ma możliwości oznaczenia fd flagą close-on-exec , ma to jednak efekt uboczny wycieku tego fd do innych poleceń.

Zobacz także ten plik locvar.sh, który zawiera kilka funkcji służących do implementacji lokalnego zasięgu zmiennych i funkcji w skryptach POSIX, a także udostępnia funkcje trace_fni untrace_fnfunkcje, aby uczynić je xtrace d lub nie.

Stéphane Chazelas
źródło
Słodkie! Chciałem sprawdzić, czy są jakieś modyfikatory, które mógłbym zastosować do samej funkcji, ale nie myślałem o zwykłym przekierowaniu stderr. Dzięki!
clacke
1
Btw, stchaz.free.fr/which_interpreter z tej samej strony jest niesamowity i niepokojący. :-)
clacke
A teraz wróciłem tu ponownie po drugą metodę, zestaw wyciszający + x bez wyciszania użytecznego wyjścia stderr. Dzięki jeszcze raz!
clacke
2

Powodem, dla którego set +xjest drukowane jest to, że set -x„wypisz polecenie, które zamierzasz uruchomić, z rozszerzeniami, przed uruchomieniem . Więc powłoka nie wie, że chcesz, aby nie drukowała rzeczy, dopóki nie wydrukuje wiersza z informacją, że nie drukować. Według mojej najlepszej wiedzy nie ma sposobu, aby temu zapobiec.

Jenny D.
źródło
0

Oto rozwiązanie, którego szukałeś:

function xtrace() {
  # Print the line as if xtrace was turned on, using perl to filter out
  # the extra colon character and the following "set +x" line.
  (
    set -x
    # Colon is a no-op in bash, so nothing will execute.
    : "$@"
    set +x
  ) 2>&1 | perl -ne 's/^[+] :/+/ and print' 1>&2
  # Execute the original line unmolested
  "$@"
}

Oryginalne polecenie jest wykonywane w tej samej powłoce z transformacją tożsamości. Tuż przed uruchomieniem otrzymujesz nierekurencyjny xtrace argumentów. Pozwala to na śledzenie poleceń, które są dla Ciebie ważne, bez spamowania operatora za pomocą duplikatów kopii każdego polecenia „echo”.

# Example
echo "About to do something complicated ..."
xtrace do_something_complicated
Dave Dopson
źródło
Lub, aby uniknąć perl(i problemów z poleceniami wieloliniowymi):+() { :;} 2> /dev/null; xtrace() { (PS4=; set -x; + "$@";{ set +x; } 2> /dev/null); "$@";}
Stéphane Chazelas
Nie, przepraszam, unix.stackexchange.com/a/60049/17980 to rozwiązanie, którego szukałem. :-) Czy set -xmanewry kupują mi coś w porównaniu do zwykłych printf >&2 '+ %s\n' "$*"?
clacke
Jak w:xtrace() { printf >&2 '+ %s\n' "$*"; "$@"; }
clacke