Podstawienie procesu i potok

Zastanawiałem się, jak zrozumieć następujące kwestie:

Przesunięcie standardowego wyjścia polecenia do wejścia standardowego innego jest potężną techniką. Ale co, jeśli chcesz potokować standardowe wyjście wielu poleceń? I tu właśnie pojawia się podstawianie procesów.

Innymi słowy, czy podstawianie procesów może zrobić wszystko, co potok?

Co może zrobić podstawianie procesu, ale potok nie może?

Odpowiedź

Dobry sposób na zrozumienie różnica między nimi polega na wykonaniu małego eksperymentu w wierszu poleceń. Pomimo wizualnego podobieństwa w użyciu znaku <, robi coś zupełnie innego niż przekierowanie lub kreska.

Użyjmy date polecenie do testowania.

$ date | cat Thu Jul 21 12:39:18 EEST 2011 

To bezsensowny przykład, ale pokazuje, że cat zaakceptował wyjście date na STDIN i wypluł je z powrotem. Te same wyniki można osiągnąć przez podstawienie procesu:

$ cat <(date) Thu Jul 21 12:40:53 EEST 2011 

Jednak to, co wydarzyło się za kulisami, było inne. Zamiast otrzymać strumień STDIN, do cat przekazano nazwę pliku, który musiał zostać otwarty i przeczytaj. Możesz zobaczyć ten krok, używając echo zamiast cat.

$ echo <(date) /proc/self/fd/11 

Gdy cat otrzymał nazwę pliku, odczytywał za nas zawartość pliku. Z drugiej strony, echo właśnie pokazało nam nazwę pliku, który został przekazany. Ta różnica stanie się bardziej oczywista, jeśli dodasz więcej podstawień:

$ cat <(date) <(date) <(date) Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 $ echo <(date) <(date) <(date) /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 

To można łączyć podstawianie procesu (co generuje plik) i przekierowanie wejścia (co łączy plik ze STDIN):

$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011 

Wygląda prawie tak samo, ale tym razem do cat został przekazany strumień STDIN zamiast nazwy pliku. Możesz to zobaczyć, próbując to z echo:

$ echo < <(date) <blank> 

Ponieważ echo nie czyta STDIN i żaden argument nie został przekazany, nic nie otrzymujemy.

Potoki i przekierowania wejściowe wypychają zawartość do strumienia STDIN. Podstawianie procesu uruchamia polecenia, zapisuje ich dane wyjściowe w specjalnym pliku tymczasowym, a następnie przekazuje tę nazwę pliku w miejsce polecenia. Jakiekolwiek polecenie, którego używasz, traktuje je jako nazwę pliku. Zauważ, że utworzony plik nie jest zwykłym plikiem, ale nazwanym potokiem, który jest usuwany automatycznie, gdy nie jest już potrzebny.

Komentarze

  • Jeśli ja rozumiane poprawnie, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 mówi, że podstawianie procesu tworzy pliki tymczasowe, a nie nazwane potoki. O ile wiem named nie twórz plików tymczasowych. Zapisywanie w potoku nigdy nie obejmuje zapisywania na dysku: stackoverflow.com/a/6977599/788700
  • Wiem, że ta odpowiedź jest prawidłowa ' ponieważ używa słowa grok : D
  • @Adobe, możesz potwierdzić, czy tymczasowe podstawienie procesu pliku to nazwany potok z: [[ -p <(date) ]] && echo true. Daje to true, gdy uruchamiam go z bash 4.4 lub 3.2.

Odpowiedź

Oto trzy rzeczy, które możesz zrobić z podstawianiem procesów, które w innym przypadku są niemożliwe.

Wiele wejść procesu

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls) 

Tam po prostu nie da się tego zrobić za pomocą potoków.

Zachowanie STDIN

Powiedzmy, że masz:

curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!" 

I chcesz go uruchomić bezpośrednio. Następujące zawodzi żałośnie. Bash używa już STDIN do odczytu skryptu, więc inne dane wejściowe są niemożliwe.

curl -o - http://example.com/script.sh | bash 

Ale ten sposób działa doskonale.

bash <(curl -o - http://example.com/script.sh) 

Podstawianie procesów wychodzących

Należy również zauważyć, że podstawianie procesów działa również w drugą stronę. Możesz więc zrobić coś takiego:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt ) 

To trochę zawiły przykład, ale wysyła stdout do /dev/null, jednocześnie przesyłając stderr do skryptu seda w celu wyodrębnienia nazw plików, dla których ” Odmowa uprawnień ” błąd, a następnie wysyła TE wyniki do pliku.

Zwróć uwagę, że pierwsze polecenie i przekierowanie stdout są w nawiasach ( podpowłoka ) tak, że tylko wynik tego polecenia zostanie wysłany do /dev/null i nie zadziała z resztą wiersza.

Komentarze

  • Warto ' zauważyć, że w diff przykład, warto zająć się przypadkiem, w którym cd może zawieść: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  • ” podczas przetwarzania stderr „: isn ' t zwróć uwagę, że to nie potok, ale przechodzenie przez plik FIFO?
  • @Gauthier no; polecenie zostanie zastąpione nie przez kolejkę FIFO, ale przez odniesienie do deskryptora pliku. Więc ” echo < (echo) ” powinno dać coś takiego jak ” / dev / fd / 63 „, czyli urządzenie znaków specjalnych, które czyta lub zapisuje z FD numer 63.

Odpowiedź

Przypuszczam, że mówisz o bash lub innej zaawansowanej powłoce, ponieważ posix powłoka nie ma podstawiania procesów .

bash raporty stron podręcznika:

Podstawianie procesów
Podstawianie procesów jest obsługiwane w systemach obsługujących nazwane potoki (FIFO) lub metodę / dev / fd nazywania otwartych plików. Przybiera postać < (lista) lub> (lista). Lista procesów jest uruchamiana z wejściem lub wyjściem podłączonym do FIFO lub jakiegoś pliku w / dev / fd. W wyniku rozwinięcia nazwa tego pliku jest przekazywana jako argument do aktualnego polecenia. Jeśli używana jest forma> (lista), zapis do pliku zapewni dane wejściowe dla listy. Jeśli używana jest forma < (lista), plik przekazany jako argument powinien zostać odczytany, aby uzyskać wynik listy.

Jeśli jest dostępny, podstawianie procesu jest wykonywana jednocześnie z interpretacją parametrów i zmiennych, podstawianiem poleceń i interpretacją wyrażeń arytmetycznych.

Innymi słowy, z praktycznego punktu widzenia, możesz użyć wyrażenie takie jak poniższe

<(commands) 

jako nazwa pliku dla innych poleceń wymagających pliku jako parametru. Lub możesz użyć przekierowania dla takiego pliku:

while read line; do something; done < <(commands) 

Wracając do pytania, wydaje mi się, że podstawianie procesów i potoki nie mają wiele wspólnego.

Jeśli chcesz potokować sekwencyjnie wyjście wielu poleceń, możesz użyć jednej z następujących form:

(command1; command2) | command3 { command1; command2; } | command3 

ale może również użyć przekierowania przy podstawianiu procesu

command3 < <(command1; command2) 

na koniec, jeśli command3 akceptuje parametr pliku (w zastępstwie stdin )

command3 <(command1; command2) 

Komentarze

  • so < ( ) i < < () daje ten sam efekt, prawda?
  • @solfish: not exacllty: the first can być używane wszędzie tam, gdzie oczekiwana jest nazwa pliku, druga to przekierowanie wejściowe dla tej nazwy pliku

Odpowiedź

Jeśli polecenie przyjmuje listę plików jako argumenty i przetwarza te pliki jako dane wejściowe (lub dane wyjściowe, ale rzadko), każdy z tych plików może być nazwanym potokiem lub pseudo-plikiem / dev / fd dostarczanym w sposób przezroczysty przez podstawianie procesu:

$ sort -m <(command1) <(command2) <(command3) 

To potokuje dane wyjściowe trzech poleceń do sortowania, ponieważ sort może pobrać listę plików wejściowych w wierszu poleceń.

Komentarze

  • IIRC składnia < (polecenia) jest funkcją tylko dla basha.
  • @Philomath: It ' jest w ZSH też.
  • Cóż, ZSH ma wszystko … (lub przynajmniej próbuje).
  • @Philomath: Jak zaimplementowano podstawianie procesów w innych powłokach?
  • @Philomath <(), podobnie jak wiele zaawansowanych funkcji powłoki, pierwotnie był funkcją ksh i został przyjęty przez bash i zsh. psub to konkretnie funkcja ryb, nie ma to nic wspólnego z POSIX.

Odpowiedź

Należy zauważyć, że podstawianie procesu nie jest ograniczone do postaci <(command), która używa wyniku command jako plik. Może mieć postać >(command), która również dostarcza plik jako dane wejściowe do command. Jest to również wspomniane w cytacie z podręcznika bash w odpowiedzi @enzotib.

W powyższym przykładzie date | cat, polecenie, które używa podstawienia procesu form >(command), aby osiągnąć ten sam efekt, byłoby

date > >(cat) 

Zwróć uwagę, że > przed >(cat) jest konieczne. Można to ponownie jasno zilustrować za pomocą echo, jak w odpowiedzi @Caleb.

$ echo >(cat) /dev/fd/63 

Zatem bez dodatkowego > date >(cat) byłoby to samo co date /dev/fd/63, który wypisze komunikat na stderr.

Załóżmy, że masz program, który przyjmuje tylko nazwy plików jako parametry i nie przetwarza stdin lub stdout.Aby to zilustrować, użyję nadmiernie uproszczonego skryptu psub.sh. Treść psub.sh to

#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2" 

Zasadniczo sprawdza, czy oba argumenty są plikami (niekoniecznie zwykłymi plików) i jeśli tak jest, zapisz pierwsze pole każdego wiersza "$1" do "$2" używając awk. Następnie polecenie, które łączy w sobie wszystko, co do tej pory zostało wspomniane, to

./psub.sh <(printf "a a\nc c\nb b") >(sort) 

Spowoduje to wydrukowanie

a b c 

i jest równoważne z

printf "a a\nc c\nb b" | awk "{print $1}" | sort 

, ale poniższe nie będą działać i musimy tutaj użyć podstawiania procesów,

printf "a a\nc c\nb b" | ./psub.sh | sort 

lub jego odpowiednik

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort 

Jeśli ./psub.sh również czyta stdin poza tym, co zostało wspomniane powyżej, to taka równoważna forma nie istnieje iw takim przypadku nie ma nic, czego możemy użyć zamiast podstawienia procesu (oczywiście można też użyć nazwany potok lub plik tymczasowy, ale to już inna historia).

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *