Mam program, który zapisuje informacje do stdout
i stderr
, i muszę przejść grep
przez to, co nadchodzi do stderr , ignorując standardowe wyjście .
Oczywiście mogę to zrobić w 2 krokach:
command > /dev/null 2> temp.file
grep 'something' temp.file
ale wolałbym móc to zrobić bez plików tymczasowych. Czy są jakieś sprytne sztuczki do pipingu?
command 2| othercommand
. Bash jest tak doskonały, że rozwój zakończył się w 1982 roku, więc obawiam się, że nigdy go nie zobaczymy.Odpowiedzi:
Najpierw przekieruj stderr na stdout - potok; następnie przekieruj stdout na
/dev/null
(bez zmiany dokąd zmierza stderr):Szczegółowe informacje na temat przekierowania we / wy w całej jego różnorodności znajdują się w rozdziale Przekierowania w podręczniku użytkownika Bash.
Należy zauważyć, że sekwencja przekierowań we / wy jest interpretowana od lewej do prawej, ale potoki są konfigurowane przed interpretacją przekierowań we / wy. Deskryptory plików, takie jak 1 i 2, są odniesieniami do otwartych opisów plików. Operacja
2>&1
powoduje, że deskryptor pliku 2 aka stderr odnosi się do tego samego otwartego opisu pliku, co deskryptor pliku 1 aka stdout, do którego obecnie się odnosi (patrzdup2()
iopen()
). Operacja>/dev/null
następnie zmienia deskryptor pliku 1, tak aby odwoływał się do otwartego opisu pliku/dev/null
, ale nie zmienia to faktu, że deskryptor pliku 2 odnosi się do otwartego opisu pliku, do którego pierwotnie wskazywał deskryptor pliku 1 - mianowicie potok.źródło
command 2> /dev/stdout 1> /dev/null | grep 'something'
/dev/stdout
et al lub użyć/dev/fd/N
. Będą nieznacznie mniej wydajne, chyba że powłoka traktuje je jako specjalne przypadki; czysta notacja numeryczna nie wymaga dostępu do plików według nazwy, ale korzystanie z urządzeń oznacza wyszukiwanie nazw plików. Czy można to zmierzyć, jest dyskusyjne. Podoba mi się zwięzłość notacji numerycznej - ale używam jej od tak dawna (ponad ćwierć wieku; ouch!), Że nie mam kwalifikacji, by oceniać jej zalety we współczesnym świecie.2>&1
, co oznacza „podłącz stderr do deskryptora pliku, do którego obecnie zmierza stdout ”. Druga operacja to „zmień standard, więc idzie do/dev/null
”, pozostawiając stderr idąc do pierwotnego standardu, czyli rury. Powłoka najpierw dzieli rzeczy na symbolu potoku, więc przekierowanie potoku następuje przed przekierowaniami2>&1
lub>/dev/null
, ale to wszystko; pozostałe operacje są wykonywane od lewej do prawej. (Od prawej do lewej nie działa.)/dev/null
na odpowiednik Windowsnul
).Lub aby zamienić dane wyjściowe ze standardowego błędu i standardowego wyjścia, użyj:
Spowoduje to utworzenie nowego deskryptora pliku (3) i przypisanie go do tego samego miejsca co 1 (standardowe wyjście), a następnie przypisanie fd 1 (standardowe wyjście) do tego samego miejsca co fd 2 (błąd standardowy) i na koniec przypisanie fd 2 (błąd standardowy ) w to samo miejsce, co fd 3 (wyjście standardowe).
Standardowy błąd jest teraz dostępny jako standardowe wyjście, a stare standardowe wyjście jest zachowywane jako standardowy błąd. Może to być przesada, ale mam nadzieję, że poda więcej szczegółów na temat deskryptorów plików Bash (dla każdego procesu dostępnych jest dziewięć).
źródło
3>&-
zamknięcie zapasowego deskryptora, który stworzyłeś ze standardowego interfejsustderr
i inny, który ma kombinacjęstderr
istdout
? Innymi słowy, czy możnastderr
przejść jednocześnie do dwóch różnych plików?W Bash możesz również przekierowywać do podpowłoki za pomocą podstawiania procesów :
W rozpatrywanej sprawie:
źródło
command 2> >(grep 'something' > grep.log)
grep.log zawiera te same dane wyjściowe, co ungrepped.log zcommand 2> ungrepped.log
2> >(stderr pipe >&2)
. W przeciwnym razie wyjście „rury stderr” przejdzie przez „rurkę stdlog”.2> >(...)
działa, próbowałem,2>&1 > >(...)
ale nie udało sięawk -f /new_lines.awk <in-content.txt > out-content.txt 2> >(tee new_lines.log 1>&2 )
W tym przypadku chciałem również zobaczyć, co wychodzi jako błędy na mojej konsoli. Ale STDOUT szedł do pliku wyjściowego. Więc wewnątrz podpowłoki musisz przekierować STDOUT z powrotem do STDERR w nawiasach. Podczas gdy to działa, wyjście STDOUTtee
polecenia kończy się na końcuout-content.txt
pliku. To wydaje mi się niespójne.2>&1 1> >(dest pipe)
Łącząc najlepsze z tych odpowiedzi, jeśli:
command 2> >(grep -v something 1>&2)
... wtedy wszystkie stdout są zachowywane jako stdout i wszystkie stderr są zachowywane jako stderr, ale nie zobaczysz żadnych linii w stderr zawierających ciąg „coś”.
Ma to tę wyjątkową zaletę, że nie odwraca ani nie odrzuca stdout i stderr, nie łączy ich razem ani nie używa plików tymczasowych.
źródło
command 2> >(grep -v something)
(bez1>&2
) to samo?tar cfz my.tar.gz mydirectory/ 2> >(grep -v 'changed as we read it' 1>&2)
powinno działać.O wiele łatwiej jest wizualizować rzeczy, jeśli myślisz o tym, co naprawdę dzieje się z „przekierowaniami” i „potokami”. Przekierowania i potoki w bash robią jedną rzecz: modyfikują tam, gdzie wskazują deskryptory plików procesowych 0, 1 i 2 (patrz / proc / [pid] / fd / *).
Gdy rura lub „|” operator jest obecny w wierszu poleceń, pierwszą rzeczą, która się wydarzy, jest to, że bash tworzy FIFO i wskazuje FD 1 komendy po lewej stronie na FO 0 i wskazuje FD 0 komendy po prawej stronie na tę samą FIFO.
Następnie operatory przekierowania dla każdej strony są oceniane od lewej do prawej , a bieżące ustawienia są używane za każdym razem, gdy występuje duplikacja deskryptora. Jest to ważne, ponieważ odkąd rura została skonfigurowana jako pierwsza, FD1 (lewa strona) i FD0 (prawa strona) zostały już zmienione w stosunku do tego, czym normalnie mogły być, a ich powielanie odzwierciedla ten fakt.
Dlatego po wpisaniu czegoś takiego:
Oto, co się dzieje, w kolejności:
Zatem wszystkie dane wyjściowe, które „polecenie” zapisuje na FD 2 (stderr), trafiają do potoku i są odczytywane przez „grep” po drugiej stronie. Wszystkie dane wyjściowe, które „komenda” zapisuje na FD 1 (stdout), trafiają do / dev / null.
Jeśli zamiast tego uruchom następujące polecenie:
Oto co się dzieje:
Zatem wszystkie stdout i stderr z „polecenia” idą do / dev / null. Nic nie trafia do potoku, dlatego „grep” zostanie zamknięty bez wyświetlania czegokolwiek na ekranie.
Zauważ też, że przekierowania (deskryptory plików) mogą być tylko do odczytu (<), tylko do zapisu (>) lub do odczytu-zapisu (<>).
Ostatnia uwaga. To, czy program napisze coś do FD1 lub FD2, zależy wyłącznie od programisty. Dobra praktyka programowania nakazuje, aby komunikaty o błędach trafiały do FD 2, a normalne wyjście do FD 1, ale często można znaleźć niechlujne programowanie, które łączy oba lub w inny sposób ignoruje konwencję.
źródło
Jeśli używasz Bash, użyj:
http://www.gnu.org/software/bash/manual/bashref.html#Pipelines
źródło
|&
równa się,2>&1
który łączy stdout i stderr. Pytanie wyraźnie zadawało dane wyjściowe bez standardowego wyjścia .>/dev/null |&
rozwinięcie do>/dev/null 2>&1 |
i oznacza, że stdout i-węzeł jest pusty do potoku, ponieważ nikt (# 1 # 2 oba powiązane z / dev / null i-węzeł) nie jest powiązany z stdout i-węzłem (np.ls -R /tmp/* >/dev/null 2>&1 | grep i
Da puste, alels -R /tmp/* 2>&1 >/dev/null | grep i
pozwoli # 2, który jest przywiązany do standardowego i-węzła, będzie potokował).( echo out; echo err >&2 ) >/dev/null |& grep "."
nie daje wyników (tam, gdzie chcemy „błądzić”).man bash
mówi Jeśli użyto | & ... jest skrótem dla 2> i 1 |. To niejawne przekierowanie standardowego błędu na standardowe wyjście jest wykonywane po wszelkich przekierowaniach określonych przez polecenie. Najpierw przekierowujemy FD1 polecenia na zero, a następnie przekierowujemy FD2 polecenia w miejsce wskazane przez FD1, tj. null, więc FD0 grepa nie otrzymuje danych wejściowych. Zobacz stackoverflow.com/a/18342079/69663 do wyjaśnienia bardziej dogłębne.Dla tych, którzy chcą przekierować stdout i stderr na stałe do plików, grep na stderr, ale zachowaj stdout do pisania wiadomości na tty:
źródło
Spowoduje to przekierowanie komendy 1 stderr do komendy 2 stdin, pozostawiając komendę 1 stdout bez zmian.
Zaczerpnięte z LDP
źródło
Właśnie wymyśliłem rozwiązanie do wysyłania
stdout
do jednego polecenia istderr
do drugiego, używając nazwanych potoków.Tutaj idzie.
Prawdopodobnie dobrym pomysłem jest później usunięcie nazwanych rur.
źródło
Możesz użyć powłoki rc .
Najpierw zainstaluj pakiet (jest mniej niż 1 MB).
To przykład, w jaki sposób należy odrzucić standardowe wyjście i standardowy błąd potoku, aby grep w
rc
:Możesz to zrobić bez wychodzenia z Bash:
Jak zapewne zauważyłeś, możesz określić, który deskryptor pliku ma być przesyłany potokowo, za pomocą nawiasów po potoku.
Standardowe deskryptory plików są ponumerowane jako takie:
źródło
Staram się śledzić, ale to też działa,
źródło