Pipowałem linię w skrypcie bash i chcę sprawdzić, czy potok ma dane, przed przekazaniem ich do programu.
Wyszukiwanie, o którym znalazłem, test -t 0
ale tutaj nie działa. Zawsze zwraca false. Jak więc upewnić się, że potok ma dane?
Przykład:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Wynik: fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Wynik: fill
W przeciwieństwie do standardowego / kanonicznego sposobu testowania, czy powyższy potok wytworzył wydajność? dane wejściowe należy zachować, aby przekazać je do programu. Uogólnia to Jak połączyć dane wyjściowe z jednego procesu do drugiego, ale wykonać je tylko, jeśli pierwszy ma dane wyjściowe? który koncentruje się na wysyłaniu wiadomości e-mail.
Odpowiedzi:
Nie ma sposobu, aby zajrzeć do zawartości potoku za pomocą powszechnie dostępnych narzędzi powłoki, ani nie ma sposobu, aby odczytać znak do potoku, a następnie odłożyć go z powrotem. Jedynym sposobem, aby dowiedzieć się, że potok ma dane, jest odczytanie bajtu, a następnie musisz doprowadzić ten bajt do miejsca docelowego.
Więc zrób tak: przeczytaj jeden bajt; jeśli wykryjesz koniec pliku, zrób to, co chcesz, gdy wejście jest puste; jeśli czytasz bajt, rozwidlaj to, co chcesz zrobić, gdy dane wejściowe nie są puste, wstaw do niego ten bajt i potokuj pozostałe dane.
ifne
Narzędziowy z moreutils Joey Hessa wykonuje polecenie, jeśli jego wejście nie jest pusta. Zwykle nie jest instalowany domyślnie, ale powinien być dostępny lub łatwy do zbudowania na większości wariantów Uniksa. Jeśli dane wejściowe są puste,ifne
nic nie robi i zwraca stan 0, czego nie można odróżnić od komendy uruchomionej pomyślnie. Jeśli chcesz coś zrobić, jeśli dane wejściowe są puste, musisz ustawić komendę, aby nie zwracała 0, co można zrobić, zwracając rozróżnienie statusu błędu:test -t 0
nie ma z tym nic wspólnego; sprawdza, czy standardowe wejście jest terminalem. Nie mówi w żaden sposób, czy dane wejściowe są dostępne.źródło
peek
narzędzia, które mogłoby zwrócić rzeczywiste dane z potoku, nie tylko jego ilość. (w 4.4 BSD, 386BSD itp. rury zostały zaimplementowane jako pary gniazd , ale zostało to wypatroszone w późniejszych wersjach * BSD - chociaż utrzymywały je dwukierunkowo).read -t 0
(t w tym przypadku oznacza przekroczenie limitu czasu, jeśli się zastanawiasz).Prostym rozwiązaniem jest użycie
ifne
polecenia (jeśli dane wejściowe nie są puste). W niektórych dystrybucjach nie jest instalowany domyślnie. Jest to część pakietumoreutils
w większości dystrybucji.ifne
uruchamia dane polecenie tylko wtedy, gdy standardowe wejście nie jest pusteZauważ, że jeśli standardowe wejście nie jest puste, jest przekazywane
ifne
do podanego poleceniaźródło
Stare pytanie, ale na wypadek, gdyby ktoś zetknął się z nim tak jak ja: Moim rozwiązaniem jest czytanie z przekroczeniem limitu czasu.
Jeśli
stdin
jest pusty, nastąpi powrót po 5 sekundach. W przeciwnym razie odczyta wszystkie dane wejściowe i możesz je przetworzyć w razie potrzeby.źródło
-t
niestety nie jest on częścią POSIX: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlsprawdź, czy deskryptor pliku stdin (0) jest otwarty czy zamknięty:
źródło
[ -t 0 ]
sprawdza, czy fd 0 jest otwarte dla tty , a nie to, czy jest zamknięte czy otwarte../that_script </dev/null
=> "standardowe wejście ma dane". Lub./that_script <&-
mieć naprawdę zamknięte standardowe wejście .Możesz także użyć
test -s /dev/stdin
(w jawnej podpowłoce).źródło
W bash:
Wykrywa, czy dane wejściowe zawierają dane (bez odczytywania czegokolwiek). Następnie możesz odczytać dane wejściowe (jeśli dane wejściowe są dostępne w momencie wykonania odczytu):
Uwaga: należy zrozumieć, że zależy to od czasu. Wykrywa, czy dane wejściowe zawierają już dane tylko w czasie wykonywania
read -t
.Na przykład za pomocą
wyjście jest
1
(błąd odczytu, tzn .: puste wejście). Echo zapisuje niektóre dane, ale nie jest bardzo szybkie rozpoczęcie i zapisanie pierwszego bajtu, dlategoread -t 0
zgłosi, że jego dane wejściowe są puste, ponieważ program jeszcze niczego nie napisał.źródło
bash
zrobiselect()
albo jedenioctl(FIONREAD)
, albo żaden z nich, ale nie oba, tak jak powinien, aby działało.read -t0
jest zepsuta. Nie używaj goJednym z łatwych sposobów sprawdzenia, czy w Unixie są dostępne dane do odczytu, jest
FIONREAD
użycie ioctl.Nie mogę wymyślić żadnego standardowego narzędzia, które to robi, więc oto robi to trywialny program (lepszy niż
ifne
z moreutils IMHO ;-)).Jeśli nie ma dostępnych danych na standardowym wyjściu, zostanie zakończone ze statusem 1. Jeśli są dostępne dane, uruchomi się
prog
. Jeśli nieprog
zostanie podany, wyjdzie ze statusem 0.Możesz usunąć
poll
połączenie, jeśli interesują Cię tylko dane natychmiast dostępne. Powinno to działać z większością rodzajów fds, nie tylko z rurami.fionread.c
źródło
POLLHUP
wydarzenie, aby obsłużyć pustą skrzynkę? Czy to działa, jeśli na drugim końcu potoku jest wiele opisów plików?Jeśli lubisz krótkie i tajemnicze jedno-linijki:
Użyłem przykładów z pierwotnego pytania. Jeśli nie chcesz korzystać
-q
z opcji przesyłania danych w potoku z grepźródło
Wydaje się to być rozsądną implementacją ifne w bashu, jeśli nie masz nic przeciwko czytaniu całej pierwszej linii
źródło
read
zwróci również false, jeśli dane wejściowe nie są puste, ale nie zawierają znaku nowej linii,read
wykonują pewne przetwarzanie na danych wejściowych i mogą odczytać więcej niż jedną linię, chyba że wywołasz ją jakoIFS= read -r line
.echo
nie można użyć do dowolnych danych.Działa to dla mnie przy użyciu
read -rt 0
przykład z oryginalnego pytania, bez danych:
źródło
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(fałszywie ujemny) itrue | { sleep .1; read -rt0 && echo YES; }
(fałszywie pozytywny). W rzeczywistości, bash użytkownikaread
będzie oszukać nawet przez FDS otwartych tylko do zapisu trybie:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. Jedyne, co wydaje się robić, toselect(2)
na tym fd.select
zwróci fd jako „gotowy”, jeśliread(2)
nie zablokuje się na nim, bez względu na to, czy zwróciEOF
lub błąd. Wniosek:read -t0
jest uszkodzony wbash
. Nie używaj tego.{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
nie jest fałszywy negatyw, ponieważ (w czasie wykonywania odczytu) nie ma danych wejściowych. Później (uśpienie .1) wejście to jest dostępne (dla kota).echo "" | { read -t0 && echo YES; }
drukuje TAK, aleecho "" | { read -rt0 && echo YES; }
nie.