Как эмулировать процесс подстановки в Dash?

18

В bash, я могу использовать Process Замещение и лакомство выход процесса , как если бы это был файл , сохраненный на диске:

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

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

к сожалению, замена процесса не поддерживается в dash.

Какой будет лучший способ подражать Process Substitutionв тире?

Я не хочу сохранять вывод как временный файл где-то ( /tmp/), а затем должен удалить его. Есть ли альтернативный способ?

Мартин Вегтер
источник
В зависимости от того, что вы пытаетесь сделать, вы можете использовать трубы.
Эрик Ренуф
Честно говоря, если бы переносимость не была для вас главной задачей, не проще ли было бы просто установить ее bashна свое устройство?
Аркадиуш Драбчик
1
Пример, который вы приводите в уведомлении о вознаграждении, является темой этого связанного ответа . Как показано там, упрощенная версия ответа Жиля (при условии наличия /dev/fdи использования xz -cd <file>вместо cat <file> | xz -d) может быть xz -cd "$1" | { xz -cd "$2" | { diff /dev/fd/3 /dev/fd/4; } 3<&0; } 4<&0.
Фра-Сан
1
@ fra-san - это то, что мне нужно. Вы можете сделать это ответом, если хотите. Благодарю.
Мартин Вегтер

Ответы:

4

Вопрос в текущем уведомлении о вознаграждении:

общий пример слишком сложен. Может кто-нибудь объяснить, как реализовать следующий пример?diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

кажется, здесь есть ответ .

Как показано в ответе Жиля , общая идея состоит в том, чтобы отправлять выходные данные команд «продюсера» в файлы 1 новых устройств на разных этапах конвейера, делая их доступными для команд «потребителя», которые могут принимать имена файлов в качестве аргументов ( при условии, что ваша система дает вам доступ к файловым дескрипторам как /dev/fd/X).

Самый простой способ достичь того, что вы ищете, это:

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

(Использование file1.xzвместо "$1"удобочитаемости, а xz -cdне cat ... | xz -dпотому, что достаточно одной команды).

Выход первой команды «продюсер» xz -cd file1.xzпередается в составную команду ( {...}); но вместо немедленного использования в качестве стандартного ввода следующей команды, он дублируется в дескрипторе файла 3и, таким образом, становится доступным для всего внутри составной команды as /dev/fd/3. Выходные данные второй команды «продюсер» xz -cd file2.xz, которая не использует ни своего стандартного ввода, ни чего-либо из файлового дескриптора 3, затем передаются в команду «потребителя» diff, которая считывает из своего стандартного ввода и из /dev/fd/3.

Может быть добавлено дублирование трубопровода и дескриптора файла, чтобы обеспечить файлы устройства для стольких команд «производителя», сколько необходимо, например:

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

Хотя это может быть неуместно в контексте вашего конкретного вопроса, стоит отметить, что:

  1. cmd1 <(cmd2) <(cmd3), cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0И ( cmd2 | ( cmd3 | ( cmd1 /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )имеют различные потенциальные эффекты на начальной среде выполнения.

  2. Вопреки тому, что происходит в cmd1 <(cmd2) <(cmd3), cmd3и cmd1в cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0не сможет прочитать какие-либо входные данные от пользователя. Это потребует дополнительных файловых дескрипторов. Например, чтобы соответствовать

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

    вам понадобится что-то вроде

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

1 Более подробную информацию о них можно найти в U & L, например, в Understanding / dev и его подкаталогах и файлах .

Фра-Сан -
источник
12

Вы можете воспроизвести то, что делает оболочка под капотом, выполнив сантехнику вручную. Если в вашей системе есть записи, вы можете использовать перестановку дескриптора файла: вы можете перевести/dev/fd/NNN

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

в

{ 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

Я показал более сложный пример, чтобы проиллюстрировать несколько входов и выходов. Если вам не нужно читать из стандартного ввода, и единственная причина, по которой вы используете подстановку процессов, заключается в том, что команде требуется явное имя файла, вы можете просто использовать /dev/stdin:

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

Без , вам нужно использовать именованный канал . Именованный канал - это запись каталога, поэтому вам нужно где-то создать временный файл, но этот файл - просто имя, он не содержит никаких данных./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/9не эквивалентны в <&8 >&9Linux и их следует избегать.
Стефан
@ Жиль - меня смущает сложный пример. Не могли бы вы показать , как эмулировать: diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)?
Мартин Вегтер
3

Может кто-нибудь объяснить, как реализовать следующий пример?
diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

Я думаю, что именованные каналы проще перенимать, чем перенаправления, поэтому в простейших терминах:

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

p1и p2являются временными именованными каналами, они могут быть названы как угодно.

Было бы разумнее создать каналы /tmp, например, в каталоге, как показывает ответ Жиля , и заметьте, что вам catздесь не нужно , поэтому:

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"

(Вероятно, вы могли бы обойтись и без кавычек, поскольку mktemp, скорее всего, создадите «красивые» имена файлов.)

У конвейерного решения есть предостережение о том, что если основная команда ( diff) умирает до прочтения всех входных данных, фоновые процессы можно оставить в подвешенном состоянии.

ilkkachu
источник
0

Как насчет:

cat "$2" | xz -d | diff /dev/sdtin /dev/stderr 2<<EOT
`cat "$1" | xz -d`
EOT
defdefred
источник