Mam plik binarny (którego nie mogę modyfikować) i mogę:
./binary < file
Mogę też zrobić:
./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF
Ale
cat file | ./binary
daje mi błąd. Nie wiem, dlaczego to nie działa z rurą. We wszystkich 3 przypadkach zawartość pliku jest podawana na standardowe wejście binarne (na różne sposoby):
- bash czyta plik i przekazuje go do standardowego pliku binarnego
- bash czyta wiersze ze standardowego wejścia (do EOF) i przekazuje je do standardowego pliku binarnego
- cat czyta i umieszcza wiersze pliku na standardowe wyjście, bash przekierowuje je na standardowe wejście binarne
Binarny nie powinien zauważyć różnicy między tymi 3, o ile je rozumiem. Czy ktoś może wyjaśnić, dlaczego trzeci przypadek nie działa?
BTW: Błąd podany przez plik binarny to:
20170116 / 125624.689 - U3000011 Nie można odczytać pliku skryptu „”, kod błędu „14”.
Ale moje główne pytanie brzmi: jaka jest różnica dla każdego programu z tymi 3 opcjami.
Oto kilka dalszych szczegółów: spróbowałem ponownie z strace i rzeczywiście wystąpiły błędy ESPIPE (nielegalne wyszukiwanie) z lseek, a następnie EFAULT (zły adres) z odczytu tuż przed komunikatem o błędzie.
Plik binarny, który próbowałem kontrolować za pomocą skryptu ruby (bez użycia plików tymczasowych), jest częścią callapi z Automic (UC4) .
źródło
isatty()
zwróci fałsz, będzie widoczne lub możliwe do zastosowania w pliku ...cat
. Wygląda na to, że nie można go użyć do połączenia dwóch plików, podobnie jak zamierzone użycie.Odpowiedzi:
W
binary
Stdin to plik otwarty w trybie tylko do odczytu. Zauważ, że wbash
ogóle nie odczytuje pliku, po prostu otwiera go do odczytu na deskryptorze pliku 0 (stdin) procesu, w którym wykonujebinary
.W:
W zależności od powłoki,
binary
stdin będzie albo usuniętym plikiem tymczasowym (AT&T ksh, zsh, bash ...), który zawiera,test\n
jak tam umieszczony przez powłokę lub koniec odczytu potoku (dash
,yash
; i powłoka zapisujetest\n
równolegle na drugim końcu rury). W twoim przypadku, jeśli używaszbash
, byłby to plik tymczasowy.W:
W zależności od powłoki,
binary
stdin będzie albo końcem odczytu rury, albo jednym końcem pary gniazd, w którym kierunek zapisu został wyłączony (ksh93) icat
zapisuje zawartośćfile
na drugim końcu.Kiedy stdin jest zwykłym plikiem (tymczasowym lub nie), można go zobaczyć.
binary
może przejść do początku lub końca, przewinąć do tyłu itp. Może takżeioctl()s
odwzorować , wykonać kilka takich jak FIEMAP / FIBMAP (jeśli użyje<>
zamiast tego<
, może obciąć / dziurkować w nim itp.).z drugiej strony pary rur i gniazd są środkami komunikacji międzyprocesowej,
binary
opróczread
danych niewiele można zrobić (chociaż są też pewne operacje, takie jak niektóre specyficzne dla rur,ioctl()
które można by na nich wykonać, a nie na zwykłych plikach) .Większość czasu, to brakujące umiejętność
seek
, która powoduje, aplikacje na niepowodzenie / skarżą się podczas pracy z rurami, ale może to być jakikolwiek inny wywołań systemowych, które obowiązują na zwykłych plików, ale nie na różnych typach plików (jakmmap()
,ftruncate()
,fallocate()
) . W Linuksie istnieje również duża różnica w zachowaniu, gdy otwierasz,/dev/stdin
gdy fd 0 jest w potoku lub w zwykłym pliku.Istnieje wiele poleceń, które obecnie nie mogą zajmować się tylko możliwy do przeszukania plików, ale kiedy to przypadek, to nie jest na ogół za pliki otwarte na ich stdin.
unzip
musi odczytać indeks zapisany na końcu pliku, a następnie szukać w pliku, aby odczytać członków archiwum. Ale tutaj plik (zwykły w pierwszym przypadku, potok w drugim) jest podawany jako argument ścieżki dounzip
iunzip
sam się otwiera (zwykle na fd innym niż 0) zamiast dziedziczenia fd już otwartego przez rodzica. Nie odczytuje plików zip ze standardowego wejścia. Stdin jest najczęściej używany do interakcji użytkownika.Jeśli uruchomisz to
binary
bez przekierowania po zachęcie interaktywnej powłoki działającej w emulatorze terminali,binary
stdin zostanie odziedziczony po nadrzędnej powłoce, która sama odziedziczy go po nadrzędnej emulatorze terminali i będzie Urządzenie pty otwarte w trybie odczytu + zapisu (coś w stylu/dev/pts/n
).Te urządzenia też nie są widoczne. Jeśli więc
binary
działa poprawnie podczas pobierania danych z terminala, być może problem nie polega na szukaniu.Jeśli ta 14 ma być błędem (kod błędu ustawiony przez nieudane wywołania systemowe), to w większości systemów byłoby to
EFAULT
( Zły adres ).read()
Wywołanie systemowe nie powiedzie się z tego błędu, jeśli poprosił, aby przeczytać na adres pamięci, który nie jest zapisywalny. Byłoby to niezależne od tego, czy fd odczytuje dane z punktów do potoku lub zwykłego pliku i ogólnie wskazuje na błąd 1 .binary
prawdopodobnie określa typ pliku otwartego na stdin (withfstat()
) i napotyka błąd, gdy nie jest to zwykły plik ani urządzenie tty.Trudno powiedzieć, nie wiedząc więcej o aplikacji. Uruchomienie go pod
strace
(lubtruss
/ lubtusc
odpowiednikiem w twoim systemie) może pomóc nam zobaczyć, co to jest wywołanie systemowe, jeśli coś tutaj nie działa.1 Scenariusz przewidziany przez Matthew Ife w komentarzu do twojego pytania brzmi tutaj bardzo realistycznie. Cytując go:
źródło
./binary < file
jest widoczne!open
i zachowuje się tak samo jak każdy edytowany plikopen
. Zdarza się, że został odziedziczony po procesie nadrzędnym, ale to nie jest tak rzadkie.open("/proc/self/fd/0", O_RDWR)
działa, nawet na usuniętych plikach. Silly me: P.echo foo>foo; (sleep 0.5; ll -L /proc/self/fd/0; strace ./a.out; ll -L /proc/self/fd/0) < foo & sleep 0.1 && rm foo
rozłącza sięfoo
przed uruchomieniem a.out z jego stdin przekierowanym zfoo
.Oto prosty przykład program, który ilustruje odpowiedź Stéphane Chazelas' stosując
lseek(2)
na wejściu:Testowanie:
Rury nie są widoczne, i to jest jedno miejsce, w którym program może narzekać na rury.
źródło
Fajka i przekierowanie to różne zwierzęta, że tak powiem. Kiedy używasz
here-doc
przekierowania (<<
) lub przekierowania stdin,<
tekst nie wychodzi z tonu - trafia on do deskryptora pliku (lub pliku tymczasowego, jeśli chcesz), i tam właśnie wskaże stdin pliku binarnego.Oto fragment
bash's
kodu źródłowego, plik redir.c (wersja 4.3):Ponieważ przekierowanie można zasadniczo traktować jako pliki, pliki binarne mogą nawigować po nich lub
seek()
łatwo przechodzić przez plik, przechodząc do dowolnego bajtu pliku.Rurociągi, ponieważ są buforami 64 KiB (przynajmniej w Linuksie) z zapisami 4096 bajtów lub mniejszymi gwarantowanymi jako atomowe, nie są widoczne, tzn. Nie można swobodnie nimi nawigować - tylko czytać sekwencyjnie. Kiedyś zaimplementowałem
tail
polecenie w Pythonie. Po przekierowaniu można znaleźć 29 milionów wierszy tekstu w mikrosekundach, ale w przypadku przekierowaniacat
nic nie da się zrobić - więc wszystko należy odczytać sekwencyjnie.Inną możliwością jest to, że plik binarny może chcieć specjalnie otworzyć plik i nie chce otrzymywać danych wejściowych z potoku. Zwykle odbywa się to poprzez
fstat()
wywołanie systemowe i sprawdzenie, czy dane wejściowe pochodzą z określonegoS_ISFIFO
typu pliku (co oznacza potok / nazwany potok).Twój konkretny plik binarny, ponieważ nie wiemy, co to jest, prawdopodobnie próbuje szukać, ale nie może szukać rur. Zaleca się zapoznanie się z jego dokumentacją, aby dowiedzieć się, co dokładnie oznacza kod błędu 14.
UWAGA : Niektóre powłoki, takie jak dash (Debian Almquist Shell, domyślnie
/bin/sh
w Ubuntu) implementująhere-doc
przekierowanie z potokami wewnętrznie , dlatego mogą nie być widoczne. Punkt pozostaje ten sam - potoki są sekwencyjne i nie można w nich łatwo nawigować, a próby tego skutkują błędami.źródło
dash
to robią. Ta odpowiedź wyjaśnia obserwowane zachowanie podczas bash, ale najwyraźniej takie zachowanie nie jest gwarantowane w innych powłokach.dash
w moim systemie. Nie wiedziałem o tym wcześniej. Dzięki za zwrócenie uwagifstat()
standardowego wejścia, aby sprawdzić, czy jest to potok.stat
przyjmuje nazwę ścieżki. Ale tak naprawdę, sama próbalseek
jest prawdopodobnie najbardziej rozsądnym sposobem ustalenia, czy fd można zobaczyć po tym, jak już jest otwarty.Główną różnicą jest obsługa błędów.
W następującym przypadku błąd jest zgłaszany
W następującym przypadku błąd nie jest zgłaszany.
Dzięki bash nadal możesz używać PIPESTATUS:
Ale jest dostępny tylko natychmiast po wykonaniu polecenia:
Jest jeszcze jedna różnica, kiedy używamy funkcji powłoki zamiast plików binarnych. W
bash
funkcji, które są częścią potoku są wykonywane w podpowłokach (z wyjątkiem ostatniego komponentu potoku, jeślilastpipe
opcja jest włączona ibash
nie jest interaktywna), więc zmiana zmiennych nie ma wpływu na powłokę nadrzędną:źródło
>
odbywa się za pomocą powłoki, ale w potoku jest to wykonywane przez polecenie, które tworzy tekst. DOBRZE. Ale w tym konkretnym pytaniu OP używa istniejącego pliku, więc nie o to chodzi, a błąd jest generowany przez plik binarny.