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.