Dlaczego podstawianie procesów BASH nie działa w przypadku niektórych poleceń?

29

Czasami podstawienie procesu nie będzie działać zgodnie z oczekiwaniami. Oto przykład:

Wkład:

gcc <(echo 'int main(){return 0;}')

Wydajność:

/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status

Wkład:

Ale działa zgodnie z oczekiwaniami, gdy jest używany z innym poleceniem:

grep main <(echo 'int main(){return 0;}')

Wydajność:

int main(){return 0;}

Zauważyłem podobne niepowodzenia w przypadku innych poleceń (tj. Polecenie oczekujące pliku z podstawienia procesu nie może używać /dev/fd/63lub podobne). Ta awaria gccjest właśnie najnowsza. Czy istnieje jakaś ogólna zasada, o której powinienem wiedzieć, aby określić, kiedy proces podstawienia zakończy się niepowodzeniem w ten sposób i nie powinien być stosowany?

Używam tej wersji BASH na Ubuntu 12.04 (widziałem to również w arch i debian):
GNU bash, wersja 4.3.11 (1) -release (i686-pc-linux-gnu)

Lotney
źródło
1
illegal seekwygląda na to odpowiedź - w |pipektóry bashwskazuje wykonywanego programu nie jest możliwy do przeszukania plików. prawdopodobnie jeśli nie uda ci echo data | command /dev/fd/0się z programem, będziesz miał podobne szczęście w / <(cmd). Nie zapewnia pliku na dysku - po prostu zastępuje argument wskazujący deskryptor pliku potoku.
mikeserv
2
W tym konkretnym przypadku, chociaż gcc może zaakceptować standardowe dane wejściowe, domyślnie używa rozszerzenia nazwy pliku do określenia języka. Spróbuj gcc -xc <(echo 'int main(){return 0;}')(co Cwyraźnie ustawia język ).
steeldriver
Zostałem skierowany tutaj w odpowiedzi na moje własne pytanie, które wydaje się być kolejnym tego przykładem. superuser.com/questions/1243405 . Dzięki za sformułowanie pytania lepiej niż byłem w stanie.
Jonathan Hartley

Odpowiedzi:

33

W wyniku podstawienia procesu powstaje specjalny plik (jak /dev/fd/63w twoim przykładzie), który zachowuje się jak koniec odczytu nazwanego potoku. Ten plik można otworzyć i odczytać, ale nie można go zapisać, nie szukać.

Polecenia, które traktują swoje argumenty jako czyste strumienie, działają, natomiast polecenia, które oczekują, że będą szukać w plikach, które zostały im przekazane (lub do nich piszą), nie będą działać. Rodzaj polecenia, że praca będzie to co uważa się zwykle za filtrem: cat, grep, sed, gzip, awk, itd ... Przykładem polecenia, które nie będzie działać to edytor podobny vilub operacja plik podobny mv.

gccchce mieć możliwość losowego dostępu do swoich plików wejściowych w celu wykrycia języka, w którym są zapisane. Jeśli zamiast tego gccpodpowiesz, jaki jest język pliku wejściowego, z przyjemnością przesyłasz plik:

gcc -x c <(echo 'int main(){return 0;}')

Działa również prostsza, bardziej prosta forma bez podstawiania procesów:

echo 'int main(){return 0;}' | gcc -x c -

Pamiętaj, że nie jest to specyficzne dla bash. Wszystkie powłoki obsługujące podstawianie procesów zachowują się w ten sam sposób.

Celada
źródło
+1 za obejście gcc, ale nie jestem pewien co do twojego zdania dotyczącego plików. <()Format powinien działać jak plik dla wszystkich zamiarów i celów. W rzeczywistości nie znam żadnych poleceń, które oczekują, że plik nie będzie zadowolony <(). Te, które nie działają, oczekują nazw plików , a nie plików. Na przykład grep -foczekuje pliku i działa dobrze z <().
terdon
4
@terndon Na pewno <() tworzy nazwę pliku (konstrukcja rozwija się /proc/self/fd/somethingw moim systemie). Ta nazwa po otwarciu działa jak koniec odczytu nazwanego potoku ( S_IFIFO), a nie zwykły plik ( S_IFREG), który obsługuje read()i inne, ale nie seek().
Celada,
7
Uwaga: zshobsługuje trzecią formę podstawiania procesów, która wykorzystuje pliki tymczasowe specjalnie do tego celu:gcc =(echo 'int main(){return 0;}')
Stéphane Chazelas,
może powiązane , ale działa z, <(echo '...')ale nie z <(git show ...). jakiś pomysł, dlaczego to może być?
Jörn Hees,
2
GCC nie „losowo uzyskuje dostęp do swoich plików wejściowych, aby wykryć, w jakim języku jest napisany”. Po prostu patrzy na rozszerzenie nazwy pliku. Jeśli nazwa pliku nie ma rozszerzenia (lub jeśli ma takie, które nie jest rozpoznawane), GCC zakłada, że ​​plik jest plikiem obiektowym lub skryptem łączącym i przekazuje go ld(który wykrywa formaty obiektów). -xnie jest wskazówką; to jest deklaracja. Jeśli określisz -x f95, GCC przekaże plik do kompilatora Fortran-95 bez względu na jego nazwę lub zawartość. Widzieć gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Overall-Options.html
rici,