Kiedy jesteś już przy szyi w aligatorach, łatwo zapomnieć, że celem było osuszenie bagna.
- popularne powiedzenie
Pytanie dotyczy echo
, a jednak większość dotychczasowych odpowiedzi koncentrowała się na tym, jak wprowadzić set +x
polecenie. Istnieje o wiele prostsze, bardziej bezpośrednie rozwiązanie:
{ echo "Message"; } 2> /dev/null
(Przyznaję, że nie pomyślałbym o tym, { …; } 2> /dev/null
gdybym nie widział tego we wcześniejszych odpowiedziach).
Jest to nieco kłopotliwe, ale jeśli masz blok kolejnych echo
poleceń, nie musisz robić tego dla każdego z osobna:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
Pamiętaj, że nie potrzebujesz średników, gdy masz nowe znaki.
Możesz zmniejszyć obciążenie związane z pisaniem, używając pomysłu kenorba na/dev/null
stałe
otwarcie na niestandardowym deskryptorze pliku (np. 3), a następnie mówienie 2>&3
zamiast przez 2> /dev/null
cały czas.
Pierwsze cztery odpowiedzi w chwili pisania tego tekstu wymagają zrobienia czegoś specjalnego (i w większości przypadków kłopotliwego) za
każdym razem, gdy robisz echo
. Jeśli naprawdę chcesz, aby wszystkie echo
polecenia pomijały ślad wykonania (a dlaczego byś tego nie zrobił?), Możesz to zrobić globalnie, bez mungowania dużej ilości kodu. Po pierwsze zauważyłem, że aliasy nie są śledzone:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(Zauważ, że następujące fragmenty skryptu działają, jeśli shebang jest #!/bin/sh
, nawet jeśli /bin/sh
jest linkiem do bash. Ale jeśli shebang jest #!/bin/bash
, musisz dodać shopt -s expand_aliases
polecenie, aby aliasy działały w skrypcie.)
Tak więc dla mojej pierwszej sztuczki:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
Teraz, kiedy mówimy echo "Message"
, wzywamy alias, który nie jest śledzony. Alias wyłącza opcję śledzenia, jednocześnie tłumiąc komunikat śledzenia z set
polecenia (przy użyciu techniki przedstawionej najpierw w odpowiedzi użytkownika 5071535 ), a następnie wykonuje rzeczywiste echo
polecenie. To pozwala nam uzyskać efekt podobny do odpowiedzi user5071535 bez konieczności edytowania kodu przy każdym echo
poleceniu. Pozostawia to jednak tryb śledzenia wyłączony. Nie możemy wstawić a set -x
do aliasu (a przynajmniej niełatwo), ponieważ alias pozwala tylko na zastąpienie łańcucha słowem; żadna część ciągu aliasu nie może zostać wstrzyknięta do polecenia
po argumentach (np "Message"
.). Na przykład, jeśli skrypt zawiera
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
wynik byłby
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
więc nadal musisz włączyć opcję śledzenia po wyświetleniu komunikatu (komunikatów) - ale tylko raz po każdym bloku kolejnych echo
poleceń:
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Byłoby miło , gdybyśmy mogli zrobić set -x
automatykę po echo
- i możemy, przy odrobinie więcej sztuczek. Ale zanim to przedstawię, zastanów się nad tym. OP zaczyna się od skryptów wykorzystujących #!/bin/sh -ex
shebang. W domyśle użytkownik może usunąć x
z shebang i mieć skrypt, który działa normalnie, bez śledzenia wykonania. Byłoby miło, gdybyśmy mogli opracować rozwiązanie, które zachowałoby tę właściwość. Kilka pierwszych odpowiedzi tutaj zawodzi tej właściwości, ponieważ echo
bezwarunkowo włączają śledzenie instrukcji po , bez względu na to, czy była już włączona.
Ta odpowiedź wyraźnie nie rozpoznaje tego problemu, ponieważ zastępuje echo
dane wyjściowe z danymi wyjściowymi śledzenia; dlatego wszystkie wiadomości znikają, jeśli śledzenie jest wyłączone. Przedstawię teraz rozwiązanie, które warunkowo włącza śledzenie po echo
instrukcji - tylko jeśli była już włączona. Obniżenie tego poziomu do rozwiązania, które bezwarunkowo odwraca śledzenie, jest trywialne i pozostawia się jako ćwiczenie.
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
jest lista opcji; konkatenacja liter odpowiadających wszystkim ustawionym opcjom. Na przykład, jeśli ustawione są opcje e
i x
, to $-
będzie mieszanka liter zawierająca e
i x
. Mój nowy alias (powyżej) zapisuje wartość $-
przed wyłączeniem śledzenia. Następnie przy wyłączonym śledzeniu przekazuje kontrolę nad funkcją powłoki. Ta funkcja wykonuje rzeczywistą operację, echo
a następnie sprawdza, czy x
opcja została włączona po wywołaniu aliasu. Jeśli opcja była włączona, funkcja ponownie ją włącza; jeśli był wyłączony, funkcja go wyłącza.
Możesz wstawić powyższe siedem wierszy (osiem, jeśli dołączasz an shopt
) na początku skryptu, a resztę pozostawić w spokoju.
To by ci pozwoliło
- aby użyć dowolnej z następujących linii shebang:
#! / bin / sh -ex
#! / bin / sh -e
#! / bin / sh –x
lub po prostu#! / bin / sh
i powinno działać zgodnie z oczekiwaniami.
- mieć podobny kod
(shebang)
polecenie 1
polecenie 2
polecenie 3
ustaw -x
polecenie 4
polecenie 5
polecenie 6
ustaw + x
polecenie 7
polecenie 8
polecenie 9
i
- Polecenia 4, 5 i 6 będą śledzone - chyba że jedno z nich jest
echo
, w takim przypadku będzie wykonywane, ale nie śledzone. (Ale nawet jeśli polecenie 5 jest echo
, polecenie 6 nadal będzie śledzone.)
- Polecenia 7, 8 i 9 nie będą śledzone. Nawet jeśli polecenie 8 jest
echo
, polecenie 9 nadal nie będzie śledzone.
- Polecenia 1, 2 i 3 będą śledzone (jak 4, 5 i 6) lub nie (jak 7, 8 i 9) w zależności od tego, czy zawiera shebang
x
.
PS Odkryłem, że w moim systemie mogę pominąć builtin
słowo kluczowe w środkowej odpowiedzi (tej, dla której jest to tylko alias echo
). Nie jest to zaskakujące; bash (1) mówi, że podczas rozwijania aliasu…
… Słowo identyczne z rozwijanym aliasem nie jest rozszerzane po raz drugi. Oznacza to, że jeden może alias ls
do ls -F
, na przykład, a bash nie próbuje rekursywnie rozwiń tekst.
Nic dziwnego, że ostatnia odpowiedź (ta z echo_and_restore
) kończy się niepowodzeniem, jeśli builtin
słowo kluczowe zostanie pominięte 1 . Ale dziwnie to działa, jeśli usunę builtin
i zmienię kolejność:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 Wydaje się, że powoduje niezdefiniowane zachowanie. widziałem
- nieskończona pętla (prawdopodobnie z powodu nieograniczonej rekurencji),
/dev/null: Bad address
komunikat o błędzie i
- zrzut rdzenia.
echo +x; echo "$*"; echo -x
w aliasie, chciałbym to zobaczyć.