Powiedzmy, że mam skrypt podobny do następującego:
useless.sh
echo "This Is Error" 1>&2
echo "This Is Output"
Mam inny skrypt powłoki:
alsoUseless.sh
./useless.sh | sed 's/Output/Useless/'
Chcę przechwycić „This Is Error” lub dowolny inny błąd standardowy z useless.sh do zmiennej. Nazwijmy to BŁĄDEM.
Zauważ, że do czegoś używam standardowego wyjścia. Chcę nadal używać stdout, więc przekierowanie stderr na stdout nie jest pomocne w tym przypadku.
Więc w zasadzie chcę to zrobić
./useless.sh 2> $ERROR | ...
ale to oczywiście nie działa.
Wiem też, że mógłbym to zrobić
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
ale to brzydkie i niepotrzebne.
Niestety, jeśli nie pojawią się tutaj żadne odpowiedzi, to będę musiał zrobić.
Mam nadzieję, że jest inny sposób.
Czy ktoś ma lepsze pomysły?
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Odpowiedzi:
Byłoby lepiej przechwycić plik błędu w ten sposób:
Powłoka rozpoznaje to i nie musi uruchamiać '
cat
', aby uzyskać dane.Większe pytanie jest trudne. Nie sądzę, aby można było to łatwo zrobić. Musiałbyś zbudować cały potok w powłoce podrzędnej, ostatecznie wysyłając końcowe standardowe wyjście do pliku, aby można było przekierować błędy na standardowe wyjście.
Zwróć uwagę, że średnik jest potrzebny (w klasycznych muszlach - Bourne, Korn - na pewno; prawdopodobnie również w Bash). Symbol „
{}
” wykonuje przekierowanie we / wy przez dołączone polecenia. Jak napisano, wychwytuje również błędysed
.źródło
/dev/null
zamiastoutfile
(Jeśli jesteś podobny do mnie, znalazłeś to pytanie przez Google i nie masz takich samych wymagań jak OP)stdout
istderr
przodu i do tyłu. Ale uważaj , jak tutaj jest powiedziane: W bash, byłoby lepiej nie zakładać, że deskryptor 3 nieużywane” .alsoUseless.sh
Umożliwi to przesłanie danych wyjściowych
useless.sh
skryptu za pomocą polecenia takiego jaksed
i zapisaniestderr
w zmiennej o nazwieerror
. Wynik potoku jest wysyłany dostdout
wyświetlenia lub do innego polecenia.Tworzy kilka dodatkowych deskryptorów plików, aby zarządzać przekierowaniami potrzebnymi w tym celu.
źródło
stderr
istdout
w zmiennych?dry_run
funkcję, która może niezawodnie wybierać między powtarzaniem swoich argumentów a ich uruchamianiem, niezależnie od tego, czy polecenie, które jest uruchamiane na sucho, jest przesyłane potokiem do innego pliku.read
nie przyjmuje danych wejściowych z potoku. Możesz użyć innych technik, aby osiągnąć to, co próbujesz pokazać.Przekierowano stderr na stdout, stdout na / dev / null, a następnie użyj backticks lub
$()
do przechwycenia przekierowanego stderr:źródło
PY_VERSION="$(python --version 2>&1)"
Istnieje wiele duplikatów tego pytania, z których wiele ma nieco prostszy scenariusz użycia, w którym nie chcesz przechwytywać stderr i stdout oraz kodu zakończenia w tym samym czasie.
działa dla typowego scenariusza, w którym w przypadku sukcesu oczekuje się prawidłowego wyniku lub komunikatu diagnostycznego na stderr w przypadku niepowodzenia.
Zauważ, że instrukcje kontrolne powłoki już sprawdzają
$?
pod maską; więc wszystko, co wyglądajest po prostu niezdarnym, jednostajnym sposobem powiedzenia
źródło
źródło
command
to zły wybór, ponieważ w rzeczywistości istnieje wbudowany plik o tej nazwie. Może toyourCommand
lub coś takiego, żeby być bardziej wyraźnym.Dla dobra czytelnika ten przepis tutaj
Jeśli chcesz złapać
stderr
niektórychcommand
dovar
można zrobićPotem masz wszystko:
Jeśli
command
jest to proste (nie coś w rodzajua | b
), możesz zostawić wnętrze z{}
daleka:Zapakowany w łatwą funkcję wielokrotnego użytku
bash
(prawdopodobnie potrzebuje wersji 3 i nowszych dlalocal -n
):Wyjaśnione:
local -n
aliasy „$ 1” (czyli zmienna dlacatch-stderr
)3>&1
używa deskryptora pliku 3, aby zapisać tam punkty wyjścia{ command; }
(lub „$ @”) wykonuje polecenie w ramach przechwytywania danych wyjściowych$(..)
2>&1
przekierowujestderr
do przechwytywania danych wyjściowych$(..)
1>&3
przekierowujestdout
z przechwytywania wyjścia z$(..)
powrotem do „zewnętrznego”,stdout
które zostało zapisane w deskryptorze pliku 3. Zauważ, żestderr
nadal odnosi się do miejsca, w którym wskazał wcześniej FD 1: Do przechwytywania wyjścia$(..)
3>&-
następnie zamyka deskryptor pliku 3, ponieważ nie jest już potrzebny, tak żecommand
nagle nie pojawia się jakiś nieznany deskryptor otwartego pliku. Zwróć uwagę, że zewnętrzna powłoka nadal ma otwarty FD 3, alecommand
go nie zobaczy.lvm
narzekają na nieoczekiwane deskryptory plików. Ilvm
narzekastderr
- właśnie to, co uchwycimy!Możesz złapać dowolny inny deskryptor pliku z tym przepisem, jeśli odpowiednio się dostosujesz. Z wyjątkiem oczywiście deskryptora pliku 1 (tutaj logika przekierowania byłaby błędna, ale dla deskryptora pliku 1 można po prostu użyć
var=$(command)
jak zwykle).Zauważ, że ten deskryptor pliku poświęca 3. Jeśli potrzebujesz tego deskryptora pliku, możesz zmienić jego numer. Należy jednak pamiętać, że niektóre powłoki (z lat 80. XX wieku) mogą być rozumiane
99>&1
jako argument,9
po którym następuje9>&1
(nie stanowi to problemubash
).Należy również zauważyć, że nie jest szczególnie łatwe skonfigurowanie tego FD 3 za pomocą zmiennej. To sprawia, że rzeczy są bardzo nieczytelne:
Uwagi:
catch-var-from-fd-by-fd var 2 3 cmd..
jest taki sam jakcatch-stderr var cmd..
shift || return
to tylko sposób na uniknięcie brzydkich błędów w przypadku, gdy zapomnisz podać poprawną liczbę argumentów. Być może zamknięcie powłoki byłoby innym sposobem (ale utrudnia to testowanie z wiersza poleceń).exec
, ale wtedy robi się naprawdę brzydka.bash
tak dobrze, że nie ma takiej potrzebylocal -n
. Jednak wtedy nie możesz używać zmiennych lokalnych i robi się to wyjątkowo brzydkie!eval
są one używane w bezpieczny sposób. Zwykleeval
jest uważany za niebezpieczny. Jednak w tym przypadku nie jest to bardziej złe niż używanie"$@"
(do wykonywania dowolnych poleceń). Jednak pamiętaj, aby używać dokładnych i poprawnych cytatów, jak pokazano tutaj (w przeciwnym razie staje się to bardzo niebezpieczne ).źródło
Oto jak to zrobiłem:
Przykład użycia:
To robi użyć pliku tymczasowego. Ale przynajmniej brzydkie rzeczy są opakowane w funkcję.
źródło
eval
. Na przykładprintf -v "$1" '%s' "$(<tmpFile)"
nie ryzykuje uruchomienia dowolnego kodu, jeśliTMPDIR
zmienna została ustawiona na złośliwą wartość (lub nazwa zmiennej docelowej zawiera taką wartość).rm -- "$tmpFile"
jest bardziej wytrzymały niżrm $tmpFile
.To ciekawy problem, na który liczyłem na eleganckie rozwiązanie. Niestety, kończy się na rozwiązaniu podobnym do pana Lefflera, ale dodam, że można wywołać bezużyteczną funkcję z wnętrza Bash, aby poprawić czytelność:
Wszystkie inne rodzaje przekierowań wyjścia muszą być poparte plikiem tymczasowym.
źródło
POSIX
STDERR można przechwycić za pomocą magii przekierowań:
Zauważ, że orurowanie STDOUT polecenia (tutaj
ls
) odbywa się wewnątrz najbardziej wewnętrznego{
}
. Jeśli wykonujesz proste polecenie (np. Nie potok), możesz usunąć te wewnętrzne nawiasy klamrowe.Nie możesz potokować poza polecenie, ponieważ potok tworzy podpowłokę w
bash
izsh
, a przypisanie do zmiennej w podpowłoce nie byłoby dostępne dla bieżącej powłoki.grzmotnąć
W programie
bash
lepiej nie zakładać, że deskryptor pliku 3 jest nieużywany:Zauważ, że to nie działa w
zsh
.Dzięki tej odpowiedzi za ogólny pomysł.
źródło
Ten post pomógł mi znaleźć podobne rozwiązanie do własnych celów:
Dopóki nasz MESSAGE nie jest pustym łańcuchem, przekazujemy go innym rzeczom. Dzięki temu będziemy wiedzieć, czy nasz format_logs.py nie powiódł się z powodu jakiegoś wyjątku w Pythonie.
źródło
Przechwyć i wydrukuj stderr
Awaria
Możesz użyć
$()
do przechwycenia stdout, ale zamiast tego chcesz przechwycić stderr. Więc zamieniasz stdout i stderr. Używanie fd 3 jako tymczasowej pamięci w standardowym algorytmie wymiany.Jeśli chcesz przechwycić ORAZ wydrukować, użyj,
tee
aby zrobić duplikat. W tym przypadku dane wyjściowe programutee
będą przechwytywane przez$()
zamiast trafiać do konsoli, ale stderr (oftee
) nadal będzie trafiać do konsoli, więc używamy go jako drugiego wyjścia zatee
pośrednictwem specjalnego pliku,/dev/fd/2
ponieważtee
oczekuje ścieżki do pliku zamiast fd numer.UWAGA: To bardzo dużo przekierowań w jednej linii, a kolejność ma znaczenie.
$()
przechwytuje stdout ztee
na końcu potoku, a sam potok kieruje stdout z./useless.sh
do stdin ztee
AFTER zamieniliśmy stdin i stdout na./useless.sh
.Korzystanie ze standardowego wyjścia ./useless.sh
OP powiedział, że nadal chce używać (nie tylko drukowania) standardowego wyjścia, na przykład
./useless.sh | sed 's/Output/Useless/'
.Żaden problem, po prostu zrób to PRZED zamianą stdout i stderr. Zalecam przeniesienie go do funkcji lub pliku (także-useless.sh) i wywołanie tego zamiast ./useless.sh w powyższym wierszu.
Jeśli jednak chcesz przechwycić stdout i stderr, myślę, że musisz wrócić do plików tymczasowych, ponieważ
$()
będzie to robić tylko jeden naraz i tworzy podpowłokę, z której nie możesz zwracać zmiennych.źródło
Powtarzając nieco odpowiedź Toma Hale'a, odkryłem, że możliwe jest zawinięcie jogi przekierowania w funkcję ułatwiającą ponowne użycie. Na przykład:
Niemal na pewno możliwe jest dalsze uproszczenie tego. Nie był testowany szczególnie dokładnie, ale wydaje się, że działa zarówno z bash, jak i ksh.
źródło
Jeśli chcesz ominąć użycie pliku tymczasowego, możesz użyć podstawiania procesów. Jeszcze nie udało mi się tego zrobić. To była moja pierwsza próba:
Potem spróbowałem
jednak
Tak więc podstawianie procesu jest ogólnie słuszne ... niestety, ilekroć zawijam
>( )
coś w STDIN,$()
próbując przechwycić to do zmiennej, tracę zawartość$()
. Myślę, że dzieje się tak, ponieważ$()
uruchamia proces podrzędny, który nie ma już dostępu do deskryptora pliku w / dev / fd, którego właścicielem jest proces nadrzędny.Zastępowanie procesów dało mi możliwość pracy ze strumieniem danych, którego nie ma już w STDERR, niestety wydaje mi się, że nie jestem w stanie manipulować nim tak, jak chcę.
źródło
./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" )
, zobaczysz wynikERROR
. Problem polega na tym, że podstawianie procesu odbywa się w powłoce podrzędnej, więc wartość ustawiona w powłoce podrzędnej nie wpływa na powłokę macierzystą.źródło
a=> b=>stderr
a
jest oceniany i przypisywany w powłoce podrzędnej, a przypisanie w powłoce podrzędnej nie wpływa na powłokę macierzystą. (Testowane na Ubuntu 14.04 LTS oraz Mac OS X 10.10.1.)GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
)SLE 11.4
też i daje efekt opisany przez @JonathanLefflerW zsh:
źródło
W celu sprawdzenia błędów poleceń:
Zainspirowani Lean Manufacturing:
źródło
if
. Pozwólcie, że opublikuję osobne rozwiązanie.Proste rozwiązanie
Będzie produkować:
źródło
Poprawa odpowiedzi YellowApple :
Jest to funkcja Bash do przechwytywania stderr do dowolnej zmiennej
stderr_capture_example.sh
:Testowanie:
Wynik:
Ta funkcja może służyć do przechwytywania zwróconego wyboru
dialog
polecenia.źródło