Як наслідувати процес заміни в тирі?


18

У bash, я можу використовувати Process Substitution і трактувати вихід процесу як би файл, збережений на диску:

$ 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і, таким чином, стає доступною для всього, що знаходиться в складеній команді як /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, і їх слід уникати там.
Стефан Шазелас

@Gilles - мене бентежить складний приклад. Скажіть, будь ласка, як наслідувати 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.