Как эмулировать процесс подстановки в 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) умирает до прочтения всех входных данных, фоновые процессы можно оставить в подвешенном состоянии.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.