Jak przekierować stdout i stderr do pliku i wyświetlić stderr na konsolę?

18

Wiem, jak przekierować do pliku i użyć tee; na poziomie podstawowym. Więc

$ alias outanderr='bash -c "echo stdout >&1; echo stderr >&2"'
# A fake "application" displaying both output and error messages.

$ outanderr 1>file      # redirect stdout to a file, display stderr
stderr

$ outanderr 2>file      # redirect stderr to a file, display stdout
stdout

$ outanderr 1>file 2>&1 # redirect both to a file, display nothing

$ outanderr | tee file; echo "-- file contents --" && cat file
# redirect stdout to a file, display both (note: order is messed up)
stderr
stdout
-- file contents --
stdout

$ outanderr 2>&1 | tee file; echo "-- file contents --" && cat file
# redirect both to a file, display both
stdout
stderr
-- file contents --
stdout
stderr

Pytanie brzmi: co napisać zamiast znaków zapytania, aby uzyskać wynik poniżej:

$ outanderr ???; echo "-- file contents --" && cat file
# redirect both to a file, display stderr
stderr
-- file contents --
stdout
stderr

Konstantty:

  • Zakładając uderzenie.
  • Kolejność powinna być przechowywana w pliku.
  • zawartość stderr jest wyświetlana w czasie rzeczywistym linia po linii, tzn. bez buforowania.
  • Można użyć osobnych plików skryptów.
  • Magia może być konieczna.
TWiStErRob
źródło
Ile outanderrmasz kontroli nad programem?
Kevin
1
@Kevin Myślę, że pytanie jest bardziej ogólne. Tutaj outanderrjest tylko alias, który wypisuje linię na stdout, a drugi na stderr. Ideą (jeśli to możliwe) jest zbudowanie ogólnego rozwiązania, które mogłoby współpracować z dowolnym programem, bez ich modyfikacji.
lgeorget
@lgeorget Rozumiem to, ale nie sądzę, aby możliwe było ścisłe spełnienie wszystkich ograniczeń w ogólnym rozwiązaniu, więc zastanawiałem się, czy możemy uzyskać konkretne.
Kevin
@Igeorget ma rację.
TWiStErRob,

Odpowiedzi:

12
2>&1 >>outputfile | tee --append outputfile

Dla łatwego testowania:

echo -n >outputfile; bash -c "echo stdout >&1; echo stderr >&2" 2>&1 >>outputfile |
  tee --append outputfile; echo "outputfile:"; cat outputfile

Edycja 1:

Działa to poprzez zapisanie stdout (tylko) do pliku, utworzenie sterr stdout tak, aby przechodziło przez potok i zapisanie przez tee swoich wyników w tym samym pliku.

Oba zapisy muszą być wykonane w trybie dołączania ( >>zamiast >), w przeciwnym razie oba zastąpiłyby dane wyjściowe pozostałych.

Ponieważ potok jest buforem, nie ma gwarancji, że dane wyjściowe pojawią się w pliku we właściwej kolejności. Nie zmieniłoby się to nawet, gdyby aplikacja była podłączona do obu deskryptorów plików (dwóch potoków). Aby zagwarantować porządek, oba wyjścia musiałyby przejść przez ten sam kanał i odpowiednio oznaczone. Lub potrzebujesz naprawdę wymyślnych rzeczy:

  1. Jeśli zarówno stdout, jak i stderr zostały przekierowane do pliku (nie do tego samego pliku!), A oba pliki znajdowały się na woluminie FUSE, moduł FUSE mógł oznaczyć każdy zapis znacznikiem czasu, aby druga aplikacja mogła poprawnie sortować dane i łączyć je dla rzeczywistego pliku wyjściowego. Lub nie oznaczasz danych, ale moduł tworzy połączony plik wyjściowy. Najprawdopodobniej nie ma jeszcze modułu FUSE, który to robi ...
  2. Można skierować zarówno stdout, jak i stderr /dev/null. Dane wyjściowe aplikacji zostaną rozdzielone przez uruchomienie strace -f -s 32000 -e trace=write. W takim przypadku musiałbyś odwrócić ucieczkę. Nie trzeba dodawać, że śledzenie aplikacji nie działa szybciej.
  3. Być może to samo można osiągnąć za pomocą istniejącego, prostego modułu FUSE i śledzenia modułu zamiast aplikacji. Może to być szybsze niż śledzenie aplikacji, ponieważ (a raczej: jeśli) moduł prawdopodobnie ma znacznie mniej wywołań systemowych niż aplikacja.
  4. Jeśli samą aplikację można zmodyfikować: aplikację można zatrzymać po każdym wyjściu (ale myślę, że jest to możliwe tylko od wewnątrz) i kontynuować tylko po otrzymaniu sygnału s (SIGUSR1 lub SIGCONT). Aplikacja odczytująca z potoku będzie musiała sprawdzić zarówno potok, jak i plik pod kątem nowych danych i wysyłać sygnał po każdym nowym danych. W zależności od rodzaju aplikacji może to być szybsze lub nawet wolniejsze niż metoda strace. BEZPIECZNIK byłby rozwiązaniem maksymalnej prędkości.
Hauke ​​Laging
źródło
1
Bah. Złap mnie w środku pisania dokładnie tej samej odpowiedzi, dlaczego nie?
Kevin
2
Uwaga: jest to warunek wyścigu, który wprowadza możliwość zamiany / błędu linii, ale nie sądzę, że można tego uniknąć.
Kevin
1
@Kevin Tak się dzieje z najlepszymi z nas, cierpiałem z tego powodu i prawie poprosiłem o funkcję „pokaż mi, że ktoś pisze” (co byłoby jednak skomplikowane). Wydaje mi się, że warunek wyścigu występuje tylko wtedy, gdy zapis do pliku (stdout) nastąpi po zapisie do potoku.
Hauke ​​Laging
Nie chcieli, wyślij oba stdouti stderrdo tee, albo ja czegoś brakuje? Myślę, że wymóg PO jest tee stderrtylko taki.
Joseph R.
@JosephR. Nie próbujesz tego?
Hauke ​​Laging