Bash: podstawienie procesu i standardowe wejście

13

Następująca linia jest oczywista:

echo "bla" | foo | bar

Ale czy te poniżej robią to samo?

echo "bla" | bar <(foo)
echo "bla" | bar < <(foo)

Które z fooi barprzeczytałem „bla” ze standardowego wejścia i dlaczego?

Mam na myśli, że oczywiście mogę po prostu kodować i sprawdzać, ale nie jestem pewien, czy jest to określone zachowanie, czy też korzystam z funkcji, na których nie powinienem polegać.

etam1024
źródło

Odpowiedzi:

9

Jest to zależne od powłoki i nieudokumentowane AFAICS. W kshoraz bash, w pierwszym przypadku, foobędą dzielić to samo standardowe wejście bar. Będą walczyć o produkcję echo.

Na przykład w

$ seq 10000 | paste - <(tr 1 X)'
1       X
2       X042
3       X043
4       X044
5       X045
[...]

Widzisz dowody, które pasteczytają co drugi blok tekstu z seqdanych wyjściowych, podczas gdy trczytają pozostałe.

Za pomocą zshpobiera zewnętrzne wejście standardowe (chyba że jest to terminal, a powłoka nie jest interaktywna, w którym to przypadku jest przekierowywana /dev/null). ksh(skąd się wziął) zshi bashsą jedynymi powłokami podobnymi do Bourne'a z obsługą zastępowania procesów AFAIK.

W echo "bla" | bar < <(foo)zauważyć, że bardo standardowego wejścia będzie rura zasilany przez wyjście foo. To dobrze określone zachowanie. W takim przypadku wydaje się, że foostdin jest rurą zasilaną przez echowszystkie ksh, zshi bash.

Jeśli chcesz zachować spójne zachowanie we wszystkich trzech powłokach i być przyszłościowym, ponieważ zachowanie może się zmienić, ponieważ nie jest udokumentowane, napisałbym to:

echo bla | { bar <(foo); }

Dla pewności foostdin jest także potokiem z echo(nie rozumiem jednak, dlaczego chcesz to zrobić). Lub:

echo bla | bar <(foo < /dev/null)

Aby upewnić się, fooże nie czyta z potoku z echo. Lub:

{ echo bla | bar 3<&- <(foo <&3); } 3<&0

Aby mieć foostandardowe wejście zewnętrzne, jak w obecnych wersjach zsh.

Stéphane Chazelas
źródło
„W takim przypadku stdin foo to rura zasilana przez echo we wszystkich ksh, zsh i bash.” Właśnie przetestowałeś to teraz ręcznie, czy jest to gdzieś udokumentowane?
etam1024
Czy „w pierwszym przypadku” w pierwszym wierszu odnosi się do „| foo | bar` lub `| pasek <(foo) `?
Volker Siegel
4

echo "bla" | foo | bar: Dane wyjściowe echo "bla"są przekierowywane do foo, których dane wyjściowe są przekierowywane bar.

echo "bla" | bar <(foo): Ten potokuje wyjście echo "bla"do bar. Ale barjest wykonywany z argumentem. Argument jest ścieżką do deskryptora pliku, do którego dane wyjściowe foozostaną wysłane. Ten argument zachowuje się jak plik zawierający dane wyjściowe foo. To nie to samo.

echo "bla" | bar < <(foo): Można założyć, że dane wyjściowe echo "bla"powinny być wysłane do, bara cała instrukcja jest równoważna z pierwszą. Ale to nie jest poprawne. Dzieje się tak: Ponieważ przekierowanie wejściowe <jest wykonywane po potoku ( |), nadpisuje je . Więc echo "bla"nie jest doprowadzony do baru. Zamiast tego wyjście foojest przekierowywane jako standardowe wejście do bar. Aby to wyczyścić, zobacz następujące polecenie i dane wyjściowe:

$ echo "bla" | cat < <(echo "foo")
foo

Jak widzisz echo "bla"został zastąpiony przez echo "foo".

Teraz zobacz to polecenie:

$ echo "bar" | cat < <(awk '{printf "%s and foo", $0}')
bar and foo

Więc echo "bar"jest przesyłany do awk, który odczytuje stdin i dodaje ciąg and foodo wyniku. To wyjście jest przesyłane do potoku cat.

chaos
źródło
A moje pytanie brzmi: „Czy fakt, że awk„ bar ”jest zdefiniowanym zachowaniem?
etam1024
Tak to jest. cmd1 < <(cmd2)zachowuje się tak samo jak cmd2 | cmd1.
chaos
Lub cmd1 | cmd2 | cmd3jest taki sam jakcmd1 | cmd3 < <(cmd2)
chaos
3
Gdyby to była wikipedia, umieściłbym na twoich wypowiedziach tag „potrzebne źródło” :) Całe moje pytanie polega na tym, że działa tak, jak mówisz, ale nie mogłem znaleźć żadnej dokumentacji na ten temat.
etam1024