Mam skrypt, który będzie uruchamiany interaktywnie przez użytkowników nietechnicznych. Skrypt zapisuje aktualizacje statusu do STDOUT, aby użytkownik miał pewność, że skrypt działa poprawnie.
Chcę, aby zarówno STDOUT, jak i STDERR zostały przekierowane do terminala (aby użytkownik mógł zobaczyć, że skrypt działa, a także sprawdzić, czy wystąpił problem). Chcę też, aby oba strumienie były przekierowywane do pliku dziennika.
Widziałem wiele rozwiązań w sieci. Niektóre nie działają, a inne są strasznie skomplikowane. Opracowałem wykonalne rozwiązanie (które podam jako odpowiedź), ale jest niezdarne.
Idealnym rozwiązaniem byłaby pojedyncza linia kodu, którą można by umieścić na początku dowolnego skryptu, który wysyła oba strumienie zarówno do terminala, jak i do pliku dziennika.
EDYCJA: Przekierowywanie STDERR do STDOUT i przesyłanie wyniku do trójnika, ale zależy to od użytkowników pamiętających o przekierowaniu i potokowaniu wyjścia. Chcę, aby logowanie było niezawodne i automatyczne (dlatego chciałbym mieć możliwość osadzenia rozwiązania w samym skrypcie).
Odpowiedzi:
Użyj „tee”, aby przekierować do pliku i ekranu. W zależności od używanej powłoki, najpierw musisz przekierować stderr na stdout za pomocą
lub
W csh jest wbudowane polecenie o nazwie „script”, które przechwytuje wszystko, co przechodzi na ekran do pliku. Zaczynasz od wpisania „script”, następnie robisz cokolwiek chcesz przechwycić, po czym wciskasz Ctrl-D, aby zamknąć plik skryptu. Nie znam odpowiednika dla sh / bash / ksh.
Ponadto, ponieważ wskazałeś, że są to twoje własne skrypty sh, które możesz modyfikować, możesz wykonać przekierowanie wewnętrznie, otaczając cały skrypt nawiasami klamrowymi lub nawiasami, na przykład
źródło
2>&1 | tee -a filename
nie zapisywałem stderr do pliku z mojego skryptu, ale działało dobrze, gdy skopiowałem polecenie i wkleiłem je do terminala! Jednak sztuczka ze wspornikiem działa dobrze.Zbliża się pół dekady później ...
Uważam, że jest to „idealne rozwiązanie”, do którego dążył PO.
Oto jedna linijka, którą możesz dodać na początku swojego skryptu Bash:
Oto mały skrypt demonstrujący jego użycie:
(Uwaga: to działa tylko z Basha to będzie. Nie praca z / bin / sh).
Zaczerpnięte stąd ; oryginał nie przechwycił, z tego co wiem, STDERR w logu. Naprawiono notatką z tego miejsca .
źródło
Wzór
To przekierowuje osobno stdout i stderr i wysyła oddzielne kopie stdout i stderr do wywołującego (którym może być twój terminal).
W zsh nie przejdzie do następnej instrukcji, dopóki
tee
s nie zakończą się .W bashu może się okazać, że kilka ostatnich wierszy wyniku pojawi się po każdej następnej instrukcji.
W obu przypadkach właściwe bity trafiają we właściwe miejsca.
Wyjaśnienie
Oto skrypt (przechowywany w ./example):
Oto sesja:
Oto jak to działa:
tee
procesy są uruchamiane, ich stdins są przypisane do deskryptorów plików. Ponieważ są zawarte w podstawieniach procesów , ścieżki do tych deskryptorów plików są zastępowane w poleceniu wywołującym, więc teraz wygląda to mniej więcej tak:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
działa, zapisując stdout do pierwszego deskryptora pliku i stderr do drugiego.W przypadku basha, po
the_cmd
zakończeniu, natychmiast pojawia się następująca instrukcja (jeśli dzwoniącym jest twój terminal, pojawi się zachęta).W przypadku zsh, po
the_cmd
zakończeniu, powłoka czeka natee
zakończenie obu procesów przed przejściem dalej. Więcej na ten temat tutaj .Pierwszy
tee
proces, który czyta ze standardowegothe_cmd
wyjścia, zapisuje kopię tego standardowego wyjścia z powrotem do wywołującego, ponieważ to właśnietee
robi. Jego dane wyjściowe nie są przekierowywane, więc zwracają je z powrotem do dzwoniącego bez zmianDrugi
tee
proces jeststdout
przekierowywany do wywołującegostderr
(co jest dobre, ponieważ jego stdin czyta zethe_cmd
standardowego polecenia). Więc kiedy zapisuje na swoje standardowe wyjście, te bity trafiają na stderr wywołującego.Dzięki temu stderr jest oddzielony od stdout zarówno w plikach, jak i w danych wyjściowych polecenia.
Jeśli pierwszy trójnik zapisze jakiekolwiek błędy, pojawią się one zarówno w pliku stderr, jak iw stderr polecenia, jeśli drugi trójnik zapisze jakiekolwiek błędy, pojawią się one tylko w stderr terminala.
źródło
tee
jest dostępny w danym systemie). Pojawia się błąd „Proces nie może uzyskać dostępu do pliku, ponieważ jest używany przez inny proces”.aby przekierować stderr na stdout, dołącz to w poleceniu:
2>&1
Do wysyłania do terminala i logowania do pliku powinieneś użyćtee
Oba razem wyglądałyby tak:
EDYCJA: Aby osadzić w swoim skrypcie, zrobiłbyś to samo. Więc twój scenariusz
skończy jako
źródło
EDYCJA: Widzę, że wykoleiłem się i ostatecznie odpowiedziałem na inne pytanie niż zadane. Odpowiedź na prawdziwe pytanie znajduje się u dołu odpowiedzi Paula Tomblina. (Jeśli z jakiegoś powodu chcesz ulepszyć to rozwiązanie, aby osobno przekierowywać stdout i stderr, możesz użyć techniki, którą tutaj opisuję).
Szukałem odpowiedzi, która zachowuje rozróżnienie między stdout a stderr. Niestety wszystkie odpowiedzi udzielone do tej pory, które zachowują to rozróżnienie, są podatne na rasy: ryzykują, że programy zobaczą niepełne dane wejściowe, jak wskazałem w komentarzach.
Myślę, że w końcu znalazłem odpowiedź, która zachowuje to rozróżnienie, nie jest podatna na rasy i nie jest też strasznie skrzypiąca.
Pierwszy blok konstrukcyjny: aby zamienić stdout i stderr:
Drugi element konstrukcyjny: gdybyśmy chcieli filtrować (np. Tee) tylko stderr, moglibyśmy to osiągnąć, zamieniając stdout i stderr, filtrując, a następnie zamieniając z powrotem:
Teraz reszta jest prosta: możemy dodać filtr stdout, albo na początku:
lub na końcu:
Aby przekonać się, że oba powyższe polecenia działają, użyłem następującego:
Wynik to:
a mój znak zachęty pojawia się natychmiast po „
teed stderr: to stderr
”, zgodnie z oczekiwaniami.Przypis o zsh :
Powyższe rozwiązanie działa w bashu (i może w kilku innych powłokach, nie jestem pewien), ale nie działa w zsh. Istnieją dwa powody niepowodzenia w zsh:
2>&3-
nie jest rozumiana przez zsh; to musi zostać przepisane jako2>&3 3>&-
Na przykład moje drugie rozwiązanie musi zostać przepisane dla zsh as
{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(które działa również w bash, ale jest okropnie rozwlekłe).Z drugiej strony możesz skorzystać z tajemniczego, wbudowanego ukrytego teeinga zsh, aby uzyskać znacznie krótsze rozwiązanie dla zsh, które w ogóle nie obsługuje tee:
(Nie zgadłbym na podstawie dokumentów, które znalazłem, że
>&1
i2>&2
są tym, co wywołuje ukryte teeing zsh; Odkryłem to metodą prób i błędów).źródło
my_function
) w zagnieżdżonym filtrowaniu stdout / stderr? Zrobiłem,{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
ale dziwne jest tworzenie pliku jako wskaźnika błędu ...Użyj
script
polecenia w swoim skrypcie (skrypt man 1)Utwórz skrypt powłoki (2 wiersze), który konfiguruje skrypt (), a następnie wywołuje polecenie exit.
Część 1: wrap.sh
Część 2: realscript.sh
Wynik:
źródło
Użyj programu tee i dup stderr do wyjścia na standardowe wyjście.
źródło
Stworzyłem skrypt o nazwie „RunScript.sh”. Zawartość tego skryptu to:
Nazywam to tak:
To działa, ale wymaga uruchamiania skryptów aplikacji za pośrednictwem zewnętrznego skryptu. To trochę niezdarne.
źródło
Rok później mam stary skrypt bash do rejestrowania czegokolwiek. Na przykład
teelog make ...
loguje się do wygenerowanej nazwy dziennika (i zobacz też sztuczkę rejestrowania zagnieżdżonych plikówmake
).źródło