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ć.Znalazłem częściowe rozwiązanie w InformIT :
wyjścia
Niestety, to wciąż odbija się echem
set +x
, ale przynajmniej jest potem cicho. więc jest to przynajmniej częściowe rozwiązanie problemu.Ale czy może być lepszy sposób na zrobienie tego? :)
źródło
W ten sposób ulepszysz własne rozwiązanie, pozbywając się
set +x
wyników:źródło
Położyć
set +x
w nawiasach, aby dotyczyło to tylko zakresu lokalnego.Na przykład:
wyprowadziłby:
źródło
set +x
wnętrze podpowłoki (lub w ogóle używanie podpowłoki) robiło coś pożytecznego. Możesz go usunąć i uzyskać ten sam wynik. Jest to przekierowanie stderr do / dev / null, które tymczasowo „wyłącza” śledzenie ... Wydaje sięecho foo1 2>/dev/null
, że itd. Byłoby równie skuteczne i bardziej czytelne.bash -x
) i już przekierowujesz&2
na null (ponieważ&3
został przekierowany na null), więc nie jestem pewien, jak ważny jest ten komentarz. Być może potrzebujemy tylko lepszego przykładu, który ilustruje twój punkt widzenia, ale przynajmniej w podanym przykładzie wydaje się, że można go uprościć bez utraty korzyści.Uwielbiam kompleksową i dobrze wyjaśnioną odpowiedź g-mana i uważam ją za najlepszą z dotychczasowych. Dba o kontekst skryptu i nie wymusza konfiguracji, gdy nie są one potrzebne. Tak więc, jeśli czytasz tę odpowiedź, sprawdź ją, wszystkie zalety są.
Jednak w tej odpowiedzi brakuje ważnego fragmentu: proponowana metoda nie będzie działać dla typowego przypadku użycia, tj. Zgłaszania błędów:
Ze względu na sposób budowy aliasu zostanie on rozszerzony do
i zgadliście,
echo_and_restore
zostajecie straceni zawsze bezwarunkowo. Ponieważset +x
część nie uruchomiła się, oznacza to, że treść tej funkcji również zostanie wydrukowana.Zmiana ostatniego
;
na&&
nie działałaby również, ponieważ w Bash||
i&&
są lewostronne .Znalazłem modyfikację, która działa dla tego przypadku użycia:
Używa podpowłoki (
(...)
części) w celu grupowania wszystkich poleceń, a następnie przekazuje ciąg wejściowy przez stdin jako ciąg tutaj (<<<
rzecz), który jest następnie drukowany przezcat -
. Jest-
to opcjonalne, ale wiesz, „ wyraźne jest lepsze niż niejawne ”.Możesz także użyć
cat -
bezpośrednio bez echa , ale podoba mi się to, ponieważ pozwala mi dodawać inne ciągi do wyniku. Na przykład zwykle używam tego w następujący sposób:A teraz działa pięknie:
źródło
Śledzenie wykonania idzie do
stderr
, filtruj w ten sposób:Kilka wyjaśnień, krok po kroku:
stderr
zostaje przekierowany… -2>
>(…)
grep
to polecenie…^
+ echo
…grep
odwraca dopasowanie… --v
stdout
; przekierowujemy gostderr
tam, gdzie należy. ->&2
Problemem jest (tak sądzę) to rozwiązanie może zsynchronizować strumienie. Z powodu filtrowania
stderr
może być trochę spóźniony w stosunku dostdout
(gdzieecho
wyjście należy domyślnie). Aby to naprawić, możesz najpierw dołączyć do strumieni, jeśli nie masz nic przeciwko, aby oba były wstdout
:Możesz zbudować takie filtrowanie w samym skrypcie, ale takie podejście z pewnością jest podatne na desynchronizację (tj. Miało to miejsce w moich testach: ślad wykonania polecenia może pojawić się po wyjściu polecenia zaraz po nim
echo
).Kod wygląda następująco:
Uruchom go bez żadnych sztuczek:
Ponownie użyj,
> >(grep -v "^+ echo ") 2>&1
aby utrzymać synchronizację kosztem dołączenia do strumieni.Inne podejście. Otrzymujesz „nieco redundantne” i dziwnie wyglądające wyjście, ponieważ twój terminal miksuje
stdout
istderr
. Te dwa strumienie są z różnych powodów zwierzętami. Sprawdź, czy analizastderr
pasuje tylko do twoich potrzeb; odrzucićstdout
:Jeśli masz w skrypcie
echo
komunikat o błędzie drukowania / błędustderr
, możesz pozbyć się nadmiarowości w sposób opisany powyżej. Pełna komenda:Tym razem pracujemy
stderr
tylko, więc desynchronizacja nie stanowi już problemu. Niestety w ten sposób nie zobaczysz śladu ani wynikówecho
tych wydruków dostdout
(jeśli w ogóle). Możemy spróbować odbudować nasz filtr w celu wykrycia przekierowania (>&2
), ale jeśli spojrzysz na niegoecho foobar >&2
,echo >&2 foobar
aecho "foobar >&2"
następnie prawdopodobnie zgodzisz się, że wszystko się komplikuje.Wiele zależy od echa, które masz w swoich skryptach. Zastanów się dwa razy, zanim zaimplementujesz jakiś złożony filtr, może się on odwrócić. Lepiej mieć trochę redundancji niż przypadkowo pominąć niektóre kluczowe informacje.
Zamiast odrzucić ślad wykonania
echo
możemy odrzucić jego wynik - i każdy wynik oprócz śladów. Aby analizować tylko ślady wykonania, spróbuj:Niezawodny? Nie. Pomyśl, co się stanie, jeśli jest
echo "+ rm -rf --no-preserve-root /" >&2
w skrypcie. Ktoś może dostać zawału serca.I w końcu…
Na szczęście istnieje
BASH_XTRACEFD
zmienna środowiskowa. Odman bash
:Możemy użyć tego w następujący sposób:
Uwaga: pierwsza linia odradza podpowłokę. W ten sposób deskryptor pliku nie pozostanie ważny ani zmienna przypisana później w bieżącej powłoce.
Dzięki temu
BASH_XTRACEFD
możesz analizować ślady wolne od echa i wszelkich innych danych wyjściowych, niezależnie od tego, jakie mogą być. To nie jest dokładnie to, czego chciałeś, ale moja analiza sprawia, że myślę, że to (ogólnie) właściwa droga.Oczywiście możesz użyć innej metody, szczególnie gdy potrzebujesz analizować
stdout
i / lubstderr
śledzić swoje ślady. Trzeba tylko pamiętać, że istnieją pewne ograniczenia i pułapki. Próbowałem je pokazać.źródło
W Makefile możesz użyć tego
@
symbolu.Przykładowe użycie:
@echo 'message'
Z tego dokumentu GNU:
źródło
./test.sh: line 1: @echo: command not found
, ale używam bash.@
działa tylko wtedy, gdy echo w plikach makefile.Edytowano 29 października 2016 na moderatora sugestie, że oryginał nie zawierał wystarczających informacji, aby zrozumieć, co się dzieje.
Ta „sztuczka” tworzy tylko jeden wiersz wyjścia komunikatu na terminalu, gdy xtrace jest aktywne:
Pierwotne pytanie brzmi: czy istnieje sposób, aby pozostawić włączone -x, ale wypisuje tylko komunikat zamiast dwóch wierszy . To jest dokładny cytat z pytania.
Rozumiem pytanie, jak „pozostawić włączone ustawienie -x ORAZ utworzyć pojedynczą linię dla wiadomości”?
Podsumowując, PO wymaga:
OP nie wymaga użycia polecenia echo . Przytoczyli go jako przykład produkcji wiadomości, używając skrótu np. Co oznacza Latin exempli gratia lub „na przykład”.
Myśląc „nieszablonowo” i rezygnując z używania echa do generowania wiadomości, zauważam, że instrukcja przypisania może spełnić wszystkie wymagania.
Wykonaj przypisanie ciągu tekstowego (zawierającego wiadomość) do nieaktywnej zmiennej.
Dodanie tej linii do skryptu nie zmienia żadnej logiki, ale tworzy pojedynczy wiersz, gdy śledzenie jest aktywne: (Jeśli używasz zmiennej $ echo , po prostu zmień nazwę na inną nieużywaną zmienną)
Na terminalu zobaczysz tylko 1 linię:
nie dwa, jak PO nie lubi:
Oto przykładowy skrypt do zademonstrowania. Zauważ, że podstawienie zmiennych w komunikacie (nazwa katalogu $ HOME 4 linie od końca) działa, więc możesz śledzić zmienne za pomocą tej metody.
A oto wyjście z uruchomienia go w katalogu głównym, a następnie w katalogu domowym.
źródło
echo "Here are the files in this directory:" *
?