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


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.