Jak emulować podstawianie procesów w Dash?


18

Za bashpomocą mogę zastąpić proces i traktować dane wyjściowe procesu tak, jakby to był plik zapisany na dysku:

$ echo <(ls)
/dev/fd/63

$ ls -lAhF <(ls)
lr-x------ 1 root root 64 Sep 17 12:55 /dev/fd/63 -> pipe:[1652825]

niestety, zastępowanie procesów nie jest obsługiwane w dash.

Jaki byłby najlepszy sposób na emulację Process Substitutionw desce rozdzielczej?

Nie chcę gdzieś zapisywać wyniku jako pliku tymczasowego ( /tmp/), a następnie muszę go usunąć. Czy istnieje alternatywny sposób?


W zależności od tego, co próbujesz zrobić, możesz używać rur.
Eric Renouf,

Szczerze mówiąc, jeśli przenośność nie jest twoim głównym zmartwieniem, czy nie byłoby łatwiej po prostu zainstalować bashna swoim urządzeniu?
Arkadiusz Drabczyk

1
Przykład podany w zawiadomieniu o nagrodach jest przedmiotem tej połączonej odpowiedzi . Jak pokazano tam, mogłaby to być uproszczona wersja odpowiedzi Gillesa (przy założeniu dostępności /dev/fdi używania xz -cd <file>zamiast cat <file> | xz -d) xz -cd "$1" | { xz -cd "$2" | { diff /dev/fd/3 /dev/fd/4; } 3<&0; } 4<&0.
fra-san

1
@ fra-san - właśnie tego potrzebowałem. Możesz udzielić odpowiedzi, jeśli chcesz. Dzięki.
Martin Vegter,

Odpowiedzi:


4

Pytanie w aktualnym ogłoszeniu o nagrodę:

ogólny przykład jest zbyt skomplikowany. Czy ktoś może wyjaśnić, jak wdrożyć następujący przykład?diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

wydaje się, że ma tutaj odpowiedź .

Jak pokazano w odpowiedzi Gillesa , ogólną ideą jest wysyłanie danych wyjściowych poleceń „producenta” do nowych plików urządzeń 1 na różnych etapach potoku, udostępniając je poleceniom „konsumenta”, które mogą przyjmować nazwy plików jako argumenty ( zakładając, że twój system daje ci dostęp do deskryptorów plików as /dev/fd/X).

Najprostszym sposobem na osiągnięcie tego, czego szukasz, jest prawdopodobnie:

xz -cd file1.xz | { xz -cd file2.xz | diff /dev/fd/3 -; } 3<&0

(Używanie file1.xzzamiast "$1"dla czytelności i xz -cdzamiast cat ... | xz -dponieważ wystarczy jedno polecenie).

Wyjście pierwszego polecenia „producent” xz -cd file1.xzjest przesyłane potokowo do polecenia złożonego ( {...}); ale zamiast zostać natychmiast wykorzystanym jako standardowe wejście następnego polecenia, jest on kopiowany do deskryptora pliku, 3a tym samym jest dostępny dla wszystkich elementów wewnątrz polecenia złożonego jako /dev/fd/3. Wyjście drugiego polecenia „producent” xz -cd file2.xz, które nie wykorzystuje ani standardowego wejścia, ani niczego z deskryptora pliku 3, jest następnie przesyłane potokowo do polecenia „konsument” diff, które odczytuje ze standardowego wejścia i z /dev/fd/3.

Można dodać potoki i powielanie deskryptorów plików w celu zapewnienia plików urządzeń dla dowolnej liczby poleceń „producenta”, np.

xz -cd file1.xz | { xz -cd file2.xz | { diff /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0

Chociaż może to być nieistotne w kontekście konkretnego pytania, warto zauważyć, że:

  1. cmd1 <(cmd2) <(cmd3), cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0I ( cmd2 | ( cmd3 | ( cmd1 /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )mają różne potencjalne skutki dla środowiska wykonywania początkowej.

  2. Wbrew temu, co dzieje się cmd1 <(cmd2) <(cmd3), cmd3i cmd1na cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0nie będzie w stanie odczytać żadnych danych od użytkownika. Będzie to wymagało dalszych deskryptorów plików. Na przykład, aby dopasować

    diff <(echo foo) <(read var; echo "$var")
    

    będziesz potrzebować czegoś takiego

    { echo foo | { read var 0<&9; echo "$var" | diff /dev/fd/3 -; } 3<&0; } 9<&0
    

1 Więcej na ich temat można znaleźć w U&L, np. W Understanding / dev oraz jego podkatalogach i plikach .


12

Możesz odtworzyć to, co robi skorupa pod maską, wykonując czynności hydrauliczne ręcznie. Jeśli twój system ma wpisy, możesz użyć tasowania deskryptorów plików: możesz tłumaczyć/dev/fd/NNN

main_command <(produce_arg1) <(produce_arg2) >(consume_arg3) >(consume_arg4)

do

{ produce_arg1 |
  { produce_arg2 |
    { main_command /dev/fd5 /dev/fd6 /dev/fd3 /dev/fd4 </dev/fd/8 >/dev/fd/9; } 5<&0 3>&1 |
    consume_arg3; } 6<&0 4>&1; |
  consume_arg4; } 8<&0 9>&1

Pokazałem bardziej złożony przykład ilustrujący wiele wejść i wyjść. Jeśli nie musisz czytać ze standardowego wejścia, a jedynym powodem, dla którego używasz podstawiania procesów, jest to, że polecenie wymaga jawnej nazwy pliku, możesz po prostu użyć /dev/stdin:

main_command <(produce_arg1)
produce_arg1 | main_command /dev/stdin

Bez tego musisz użyć nazwanego potoku . Nazwany potok jest pozycją katalogu, więc musisz gdzieś utworzyć plik tymczasowy, ale ten plik to tylko nazwa, nie zawiera żadnych danych./dev/fd/NNN

tmp=$(mktemp -d)
mkfifo "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
produce_arg1 >"$tmp/f1" &
produce_arg2 >"$tmp/f2" &
consume_arg3 <"$tmp/f3" &
consume_arg4 <"$tmp/f4" &
main_command "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
rm -r "$tmp"

2
</dev/fd/8 >/dev/fd/9nie są równoważne z <&8 >&9Linuksem i należy ich unikać.
Stéphane Chazelas

@Gilles - Jestem zdezorientowany złożonym przykładem. Czy mógłbyś pokazać jak emulować: diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)?
Martin Vegter

3

Czy ktoś może wyjaśnić, jak wdrożyć następujący przykład?
diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

Myślę, że nazwane potoki są łatwiejsze do odczytania niż przekierowania, więc najprościej mówiąc:

mkfifo p1 p2               # create pipes
cat "$2" | xz -d > p1 &    # start the commands in the background
cat "$1" | xz -d > p2 &    #    with output to the pipes
diff p1 p2                 # run 'diff', reading from the pipes
rm p1 p2                   # remove them at the end

p1i p2są tymczasowymi nazwanymi potokami, można je nazwać dowolnymi.

Byłoby mądrzejsze tworzyć rury /tmp, np. W katalogu, jak pokazuje odpowiedź Gillesa , i zauważ, że nie potrzebujesz cattutaj, więc:

tmpdir=$(mktemp -d)
mkfifo "$tmpdir/p1" "$tmpdir/p2"
xz -d < "$2" > "$tmpdir/p1" &
xz -d < "$1" > "$tmpdir/p2" &
diff "$tmpdir/p1" "$tmpdir/p2"
rm -r "$tmpdir"

(Prawdopodobnie możesz też uciec bez cudzysłowów, ponieważ mktempprawdopodobnie stworzysz „ładne” nazwy plików).

Rozwiązanie potoku ma zastrzeżenie, że jeśli główne polecenie ( diff) umrze przed odczytaniem wszystkich danych wejściowych, procesy w tle mogą pozostać zawieszone.


0

Co powiesz na:

cat "$2" | xz -d | diff /dev/sdtin /dev/stderr 2<<EOT
`cat "$1" | xz -d`
EOT
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.