Jak mogę użyć magii BASH, aby to osiągnąć?
Chcę widzieć tylko wyjście stderr na ekranie,
ale chcę, aby zarówno stdout, jak i stderr były zapisywane w pliku.
Wyjaśnienie: Chcę, aby zarówno stdout, jak i stderr znalazły się w tym samym pliku. W kolejności, w jakiej się znajdują.
Niestety żadna z poniższych odpowiedzi tego nie robi.
bash
shell
io-redirection
Andreas
źródło
źródło
Odpowiedzi:
Nawet bez przekierowania lub bez niczego
>logfile 2>&1
nie masz gwarancji, że zobaczysz wynik w kolejności generowania.Na początek stdout z aplikacji będzie buforowany liniowo (do tty) lub buforowany (do potoku), ale stderr nie jest buforowany, więc relacje między kolejnością danych wyjściowych są zerwane w odniesieniu do czytnika. Kolejne etapy w dowolnym potoku, które można wymyślić, nie otrzymają deterministycznie uporządkowanego dostępu do dwóch strumieni (są to koncepcyjnie rzeczy dziejące się równolegle, a ty zawsze podlegasz harmonogramowi - jeśli do czasu, gdy czytelnik otrzyma wycinek, pisarz już ma napisane na obu potokach, nie wiadomo, które były pierwsze).
„[T] rozkaz, żeby się zdarzyły” jest tak naprawdę znany tylko aplikacji. Kolejność produkcji w stdout / stderr jest dobrze znanym - być może klasycznym - problemem.
źródło
Podczas korzystania z konstrukcji:
1>stdout.log 2>&1
zarówno stderr, jak i stdout zostają przekierowane do pliku, ponieważ przekierowanie stdout jest konfigurowane przed przekierowaniem stderr .Jeśli odwrócisz kolejność, możesz zostać przekierowany na stdout do pliku, a następnie skopiuj stderr na stdout, aby móc go potokować
tee
.źródło
tee -a
tego samego pliku użytego1>
do zsumowania obu danych wyjściowych w tym samym pliku. Coś w stylu:./test 2>&1 1>out+err.log | tee -a out+err.log
wget -O - www.google.de
dla mnie. (porównaj z rozwiązaniem poniżej)tee -a
nie zadziała niezawodnie, aby uzyskać takie same dane wyjściowe, jakie byłyby na konsoli, gdyby nic nie zostało przekierowane. Dane wyjściowe, które przechodzą,tee
mogą być nieco opóźnione w porównaniu z danymi wyjściowymi pochodzącymi bezpośrednio z polecenia, a więc mogą pojawić się później w dzienniku.Udało mi się to osiągnąć, umieszczając następujący wiersz na górze skryptu bash:
Spowoduje to przekierowanie stdout do file
log
(1>>log
), a następnie przeniesienie stderr do filelog
(2> >(tee -a log
) i skierowanie go z powrotem do stderr (>&2)
. W ten sposób otrzymuję pojedynczy pliklog
, który pokazuje kolejno stdout i stderr, a stderr jest również wyświetlane na ekranie jak zwykle.Problem polega na tym, że wydaje się, że działa tylko wtedy, gdy dołączam do pliku. Jeśli się nie dołączę, wygląda na to, że te dwa przekierowania blokują się i otrzymuję tylko jedno wyjście jako ostatnie.
źródło
Chcesz zduplikować strumień błędów, aby pojawił się zarówno w konsoli, jak i w pliku dziennika. Narzędziem do tego jest
tee
i wszystko, co musisz zrobić, to zastosować je do strumienia błędów. Niestety, nie ma standardowej konstrukcji powłoki umożliwiającej potokowanie strumienia błędów polecenia do innego polecenia, dlatego wymagana jest niewielka zmiana układu deskryptorów plików.źródło
tee
wprowadza tutaj również opóźnienie. Nie sądzę, aby istniało rozwiązanie oparte na czystej powłoce. W rzeczywistości zastanawiam się, czy istnieje rozwiązanie bez modyfikowania aplikacji w jakikolwiek sposób.Aby napisać stderr na screen i napisać ZARÓWNO stderr i stdout do pliku - ORAZ niech wiersze stderr i stdout wychodzą w takiej samej kolejności, jak gdyby oba były zapisane na ekranie:
Okazuje się, że jest to trudny problem, zwłaszcza część o posiadaniu „tej samej sekwencji”, której można by się spodziewać, gdyby po prostu napisać je na ekranie. W prostych słowach: zapisz każdy do własnego pliku, zrób trochę magii w tle, aby oznaczyć każdą linię (w każdym pliku) dokładnym czasem, w którym linia została utworzona, a następnie: „ogon - śledź” plik stderr na ekran , ale aby zobaczyć ZARÓWNO „stderr” i „stdout” razem - po kolei - posortuj oba pliki (z oznaczeniem dokładnego czasu w każdym wierszu) razem.
Kod:
Tak, wydaje się to skomplikowane i daje w wyniku 4 pliki wyjściowe (z których 2 można usunąć). Wygląda na to, że jest to trudny problem do rozwiązania, więc wymagało kilku mechanizmów.
Na koniec, aby zobaczyć wyniki ZARÓWNO stdout i stderr w oczekiwanej sekwencji, uruchom to:
Jedynym powodem, dla którego sekwencja jest co najmniej bardzo zbliżona do tego, co by się stało, gdyby zarówno stdout, jak i stderr po prostu poszły na ekran, jest: Każda linia jest oznaczona znacznikiem czasu z dokładnością do milisekundy.
Aby zobaczyć stderr na ekranie w trakcie procesu, użyj tego:
Mam nadzieję, że to pomoże komuś, kto przybył tu długo po zadaniu pierwotnego pytania.
źródło
Niech
f
będzie to polecenie, które chcesz wykonać, a potem topowinien dać ci to, co chcesz. Na przykład
wget -O - www.google.de
wyglądałby tak:źródło
Biorąc pod uwagę to, co tutaj przeczytałem, jedynym rozwiązaniem, jakie widzę, byłoby wstawienie każdej linii STOUT lub STERR za pomocą szczeliny czasowej / znacznika czasu. date +% s przejdzie do sekund, ale to spowalnia, aby utrzymać porządek. Również dziennik musiałby jakoś zostać posortowany. Więcej pracy niż jestem w stanie.
źródło
Walczyłem z tym dokładnym wymaganiem i ostatecznie nie mogłem znaleźć prostego rozwiązania. Zamiast tego zrobiłem to:
Dołącza stdout i stderr, w odpowiedniej kolejności przeplatania, do pliku dziennika. A jeśli wystąpią jakiekolwiek błędy (określone przez status wyjścia polecenia), wysyła całe wyjście (stdout i stderr) do stdout, ponownie w odpowiedniej kolejności z przeplotem. Uznałem, że jest to rozsądny kompromis dla moich potrzeb. Jeśli chcesz mieć plik dziennika z pojedynczym uruchomieniem, a nie rosnący plik z wieloma uruchomieniami, jest to jeszcze prostsze:
źródło
stderr do podstawionego poleceniem
tee -a
, a stdout dołącza do tego samego pliku:i uwaga: też zapewnij prawidłową kolejność (ale bez stderr-show) istnieje komenda „unbuffer” z narzędzi „expect”, która symuluje tty i utrzymuje kolejność stdout / err w kolejności, w jakiej byłaby wyświetlana na terminalu:
źródło