Zbadałem prawie wszystkie dostępne podobne pytania , ale bezskutecznie.
Pozwól mi szczegółowo opisać problem:
Uruchamiam niektóre skrypty nienadzorowane, które mogą generować standardowe dane wyjściowe i standardowe linie błędów, chcę uchwycić je w dokładnej kolejności wyświetlanej przez emulator terminala, a następnie dodać do nich przedrostek „STDERR:” i „STDOUT:”.
Próbowałem na nich używać rur, a nawet podejścia opartego na epollach, ale bezskutecznie. Myślę, że rozwiązanie jest w użyciu, choć nie jestem w tym mistrzem. Zajrzałem również do kodu źródłowego VTE Gnome , ale nie było to zbyt produktywne.
Idealnie byłoby użyć Go zamiast Bash, aby to osiągnąć, ale nie byłem w stanie. Wydaje się, że rury automatycznie zabraniają utrzymywania prawidłowej kolejności linii z powodu buforowania.
Czy ktoś był w stanie zrobić coś podobnego? Czy to po prostu niemożliwe? Myślę, że jeśli emulator terminala może to zrobić, to nie jest - może przez utworzenie małego programu C obsługującego PTY (y) inaczej?
Idealnie byłoby użyć danych asynchronicznych, aby odczytać te 2 strumienie (STDOUT i STDERR), a następnie wydrukować je na nowo według moich potrzeb, ale kolejność wprowadzania jest kluczowa!
UWAGA: Zdaję sobie sprawę ze stopniowania, ale nie działa to dla mnie ze skryptami Bash i nie można go łatwo edytować w celu dodania prefiksu (ponieważ w zasadzie pakuje wiele wywołań systemowych).
Aktualizacja: dodano poniżej dwóch znaczników
(losowe opóźnienia poniżej drugiej sekundy można dodać w przykładowym skrypcie, który podałem, aby uzyskać spójny wynik)
Aktualizacja: rozwiązanie tego pytania rozwiązałoby również inne pytanie , jak wskazał @Gilles. Doszedłem jednak do wniosku, że nie jest możliwe zrobienie tego, o co prosi się tu i tam. Podczas korzystania z 2>&1
obu strumieni są one poprawnie łączone na poziomie pty / pipe, ale aby używać strumieni oddzielnie i we właściwej kolejności, należy rzeczywiście zastosować podejście stopniowane, które ingeruje w zaczepianie systemu i może być postrzegane jako brudne na wiele sposobów.
Będę chętny zaktualizować to pytanie, jeśli ktoś będzie w stanie podważyć powyższe.
źródło
Odpowiedzi:
Możesz użyć koprocesów. Proste opakowanie, które przekazuje oba dane wyjściowe polecenia do dwóch
sed
instancji (jedna dlastderr
drugiej dlastdout
), które wykonują tagowanie.Zwróć uwagę na kilka rzeczy:
Jest to magiczne zaklęcie dla wielu ludzi (w tym dla mnie) - z jakiegoś powodu (patrz link poniżej).
Nie ma gwarancji, że czasami nie zamieni kilku linii - wszystko zależy od harmonogramu koprocesów. W rzeczywistości jest prawie pewne, że w pewnym momencie tak się stanie. To powiedziawszy, jeśli zachowując porządek dokładnie taki sam, musisz przetwarzać dane zarówno z jednego, jak
stderr
istdin
tego samego procesu, w przeciwnym razie program planujący jądro może (i zrobi) bałagan.Jeśli dobrze rozumiem problem, oznacza to, że musisz poinstruować powłokę, aby przekierowała oba strumienie do jednego procesu (co można zrobić AFAIK). Kłopoty zaczynają się, gdy proces ten zaczyna od podjęcia decyzji, co należy podjąć w pierwszej kolejności - musiałby sondować oba źródła danych, a w pewnym momencie znalazłby się w stanie, w którym przetwarzałby jeden strumień, a dane docierałyby do obu strumieni przed zakończeniem. I właśnie tam się psuje. Oznacza to również, że owijanie wyjściowych systemów alarmowych
stderred
jest prawdopodobnie jedynym sposobem na osiągnięcie pożądanego rezultatu (i nawet wtedy możesz mieć problem, gdy coś stanie się wielowątkowe w systemie wieloprocesorowym).Jeśli chodzi o koprocesy, koniecznie przeczytaj doskonałą odpowiedź Stéphane'a w Jak używać polecenia coproc w Bash? dla dogłębnego wglądu.
źródło
./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpected
przez skopiowanie / wklejenie twojego skryptu)bash
z/bin/sh
(nie wiem dlaczego musiałem go tam).eval $@
jest dość wadliwy. Użyj,"$@"
jeśli chcesz uruchamiać argumenty jako dokładną linię poleceń - dodanie warstwyeval
interpretacji wprowadza kilka trudnych do przewidzenia (i potencjalnie złośliwych, jeśli przekazujesz nazwy plików lub inne treści, których nie kontrolujesz jako argumenty) i nie cytując nawet samego cytatu (dzieli nazwy ze spacjami na wiele słów, rozszerza globusy, nawet jeśli wcześniej były cytowane jako dosłowne itp.).eval
zamykać deskryptorów plików o nazwie zmiennej.exec {SEDo[1]}>&-
będzie działać tak, jak jest (tak, brak$
wcześniejszego{
było celowe).Metoda nr 1. Używanie deskryptorów plików i awk
Co powiesz na coś takiego przy użyciu rozwiązań z tego pytania i odpowiedzi SO: Czy istnieje narzędzie uniksowe do dodawania znaczników czasu do wierszy tekstu? a to SO pytania i odpowiedzi zatytułowane: potok STDOUT i STDERR do dwóch różnych procesów w skrypcie powłoki? .
Podejście
Krok 1, tworzymy 2 funkcje w Bash, które wykonają komunikat znacznika czasu po wywołaniu:
Krok 2 użyjesz powyższych funkcji, aby uzyskać pożądane wiadomości:
Przykład
Tutaj wymyśliłem przykład, który napisze
a
do STDOUT, śpi przez 10 sekund, a następnie zapisuje dane wyjściowe do STDERR. Kiedy umieścimy tę sekwencję poleceń w naszym konstrukcie powyżej, otrzymamy komunikat zgodnie z podanym przez ciebie opisem.Metoda nr 2. Korzystanie z danych wyjściowych adnotacji
Jest to narzędzie zwane
annotate-output
częściądevscripts
pakietu, które zrobi to, co chcesz. Jedynym ograniczeniem jest to, że musi uruchamiać skrypty dla Ciebie.Przykład
Jeśli umieścimy powyższą przykładową sekwencję poleceń w skrypcie o nazwie
mycmds.bash
tak:Następnie możemy uruchomić go w następujący sposób:
Format wyjściowy można kontrolować dla części znacznika czasu, ale nie dalej. Ale jest to wynik podobny do tego, czego szukasz, więc może pasować do rachunku.
źródło
stderred
tobą nie może łatwo ustalić granic linii (próba taka byłaby hackerska). Chciałem zobaczyć, czy ktoś może mi pomóc z tym problemem, ale najwyraźniej każdy chce zrezygnować z jednego ograniczenia ( porządku ), które jest podstawą pytania