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:
my_command 3>&1 1>&2 2>&3-
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:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
Teraz reszta jest prosta: możemy dodać filtr stdout, albo na początku:
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
lub na końcu:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
Aby przekonać się, że oba powyższe polecenia działają, użyłem następującego:
alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'
Wynik to:
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
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:
- składnia
2>&3-
nie jest rozumiana przez zsh; to musi zostać przepisane jako2>&3 3>&-
- w zsh (w przeciwieństwie do innych powłok), jeśli przekierujesz deskryptor pliku, który jest już otwarty, w niektórych przypadkach (nie do końca rozumiem, jak decyduje), zamiast tego zachowuje się wbudowany w tee. Aby tego uniknąć, przed przekierowaniem musisz zamknąć każdy plik fd.
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:
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(Nie zgadłbym na podstawie dokumentów, które znalazłem, że >&1
i 2>&2
są tym, co wywołuje ukryte teeing zsh; Odkryłem to metodą prób i błędów).