Utworzyłem plik testowy o nazwie „test”, który zawiera następujące elementy:
xxx
yyy
zzz
Uruchomiłem polecenie:
(sed '/y/ q'; echo aaa; cat) < test
i mam:
xxx
yyy
aaa
zzz
Potem pobiegłem:
cat test | (sed '/y/ q'; echo aaa; cat)
i dostał:
xxx
yyy
aaa
Pytanie
sed
czyta i drukuje, aż napotka linię z „y”, a następnie zatrzymuje się. W pierwszym przypadku, ale nie drugim, kot czyta i drukuje resztę.
Czy ktoś może wyjaśnić, jakie zjawisko kryje się za tą różnicą w zachowaniu?
Zauważyłem też, że działa to w ten sposób w Ubuntu 16.04 i Centos 6, ale w Centos 7 żadne polecenie nie wypisuje „zzz”.
cat
(w podpowłoce) może ponownie użyć deskryptora pliku w pierwszym przypadku, ponieważ stdin jest powiązany z prawdziwym plikiem. W drugim przypadku stdin pochodzi z potoku, a nie z prawdziwego pliku. Pamiętaj, że również(sed '/y/ q'; echo aaa; cat) < <(cat test)
nie drukujezzz
.(head -n1; head -n1) < test
icat test | (head -n1; head -n1)
Odpowiedzi:
Kiedy plik wejściowy jest widoczny (jak czytanie ze zwykłego pliku) lub nie widoczny (jak czytanie z potoku),
sed
(i inne standardowe narzędzia) będą zachowywać się inaczej (przeczytajINPUT FILES
sekcję w tym linku ).Cytat z dokumentu:
Więc w:
sed
wykonałq
polecenie uit przed osiągnięciem EOF, więc pozostawił przesunięcie pliku na początkuzzz
linii, więccat
może kontynuować drukowanie pozostałych linii (GNU sed nie jest zgodny z POSIX w niektórych warunkach, patrz poniżej).Kontynuując od dokumentu:
W takim przypadku zachowanie nie jest określone. Większość standardowych narzędzi, w tym
sed
, zużywa dane wejściowe w jak największym stopniu. Odczytujeyyy
linię iq
uit bez przywracania przesunięcia pliku, więc nic nie pozostaniecat
.GNU
sed
nie jest zgodne ze standardem, zależy od implementacji stdio systemu i wersji glibc:Tutaj wynik uzyskano z Mac OSX 10.11.6, maszyn wirtualnych Centos 7.2 - glibc 2.17, Ubuntu 14.04 - glibc 2.19, które są uruchomione na Openstack z backendem CEPH.
W tych systemach można użyć
-u
opcji, aby osiągnąć standardowe zachowanie:a dla rury:
co prowadzi do strasznie nieefektywnej wydajności, ponieważ
sed
musi czytać jeden bajt na raz. Częściowe wyjście zstrace
:źródło
sed
zależy to od implementacji stdio systemu. W systemach GNU (z GNU libc) GNUsed
będzie zgodne, tak jakexit()
będzie szukać plików zarządzanych przez stdio.sed
nie jest zgodny, mój laptop manjaro ma, wszystkie mają tę samąsed
wersję 4.2.2strace -f sh -c '{ sed "/y/q"; echo aaa; cat; } <test'
pokazują, że nielseek()
przeprowadzono, podczas gdy w moim manjarolseek()
nazwano wcześniejexit_group()
.main() { char buf[999]; gets(buf); }'
programu.