Как вы можете различать два конвейера в Bash?

Как можно использовать два конвейера разница без использования временных файлов в Bash? Допустим, у вас есть два командных конвейера:

foo | bar
baz | quux

И вы хотите найти diff на их выходах. Одно из решений, очевидно, заключалось бы в следующем:

foo | bar > /tmp/a
baz | quux > /tmp/b
diff /tmp/a /tmp/b

Можно ли сделать это без использования временных файлов в Bash? Вы можете избавиться от одного временного файла, подключив один из конвейеров к diff:

foo | bar > /tmp/a
baz | quux | diff /tmp/a -

Но вы не можете передать оба конвейера в diff одновременно (по крайней мере, не очевидным образом). Есть ли какой-нибудь хитрый трюк с /dev/fd, чтобы сделать это без использования временных файлов?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
146
0
32 023
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Однострочный файл с двумя tmp-файлами (не то, что вы хотите) будет:

 foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt

С трепать вы можете попробовать:

 diff <(foo | bar) <(baz | quux)

 foo | bar | diff - <(baz | quux)  # or only use process substitution once

Вторая версия будет более четко напоминать вам, какой ввод был какой, показывая
-- /dev/stdin против ++ /dev/fd/63 или что-то в этом роде, вместо двух пронумерованных файловых дисков.


В файловой системе не появится даже именованный канал, по крайней мере, в операционных системах, где bash может реализовать подстановку процессов, используя такие имена файлов, как /dev/fd/63, чтобы получить имя файла, которое команда может открыть и прочитать, чтобы фактически прочитать из уже открытого файлового дескриптора, который bash настроить перед выполнением команды. (т.е. bash использует pipe(2) перед вилкой, а затем dup2 для перенаправления с вывода quux на дескриптор входного файла для diff на fd 63.)

В системе без «волшебных» /dev/fd или /proc/self/fd bash может использовать именованные каналы для реализации подстановки процессов, но он, по крайней мере, сам будет управлять ими, в отличие от временных файлов, и ваши данные не будут записаны в файловую систему.

Вы можете проверить, как bash реализует замену процесса с помощью echo <(true), чтобы напечатать имя файла вместо чтения из него. Он печатает /dev/fd/63 в типичной системе Linux. Или для получения более подробной информации о том, какие именно системные вызовы использует bash, эта команда в системе Linux будет отслеживать системные вызовы файлов и файловых дескрипторов.

strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'

Без bash вы могли бы создать именованный канал. Используйте -, чтобы указать diff читать один ввод из STDIN и использовать именованный канал в качестве другого:

mkfifo file1_pipe.txt
foo|bar > file1_pipe.txt && baz | quux | diff file1_pipe.txt - && rm file1_pipe.txt

Обратите внимание, что вы можете направить один выход в несколько входов только с помощью команды tee:

ls *.txt | tee /dev/tty txtlist.txt 

Приведенная выше команда отображает вывод ls * .txt на терминал и выводит его в текстовый файл txtlist.txt.

Но с заменой процесса вы можете использовать tee для передачи одних и тех же данных в несколько конвейеров:

cat *.txt | tee >(foo | bar > result1.txt)  >(baz | quux > result2.txt) | foobar

даже без bash вы можете использовать временный FIFO mkfifo a; cmd >a& cmd2|diff a -; rm a

unhammer 10.06.2013 14:49

Вы можете использовать обычный канал для одного из аргументов: pipeline1 | diff -u - <(pipeline2). Тогда результат будет более четко напоминать вам, какой вход был какой, показывая -- /dev/stdin против ++ /dev/fd/67 или что-то в этом роде, вместо двух пронумерованных файловых дисков.

Peter Cordes 05.03.2018 07:36

подстановка процесса (foo <( pipe )) не изменяет файловую систему. Труба - анонимный; у него нет имени в файловой системе. Для его создания оболочка использует системный вызов pipe, а не mkfifo. Используйте strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)' для отслеживания системных вызовов файлов и файловых дескрипторов, если хотите в этом убедиться. В Linux /dev/fd/63 является частью виртуальной файловой системы /proc; он автоматически содержит записи для каждого дескриптора файла и не является копией содержимого. Так что вы не можете называть это «временным файлом», если foo 3<bar.txt не считает

Peter Cordes 05.03.2018 07:47

@PeterCordes Хорошие отзывы. Я включил ваш комментарий в ответ для большей наглядности.

VonC 05.03.2018 11:00

Почему бы просто не исправить свой первый большой абзац, вместо того, чтобы оставлять ошибки и только вносить исправления? Обратите внимание, что Дэниел Кэссиди удалил свой ответ через год после публикации, предположительно потому, что он был неправильным.

Peter Cordes 05.03.2018 11:24

@PeterCordes Я оставлю вам любые правки: это то, что делает Stack Overflow интересным: любой может "исправить" ответ.

VonC 05.03.2018 11:35

Мне определенно нравится философия SO, заключающаяся в исправлении существующих ответов вместо того, чтобы всегда публиковать новые. Особенно, когда он уже принят и высоко оценен.

Peter Cordes 08.03.2018 08:31

В bash вы можете использовать подоболочки для индивидуального выполнения командных конвейеров, заключив конвейер в круглые скобки. Затем вы можете добавить к ним префикс <, чтобы создать анонимные именованные каналы, которые затем можно передать в diff.

Например:

diff <(foo | bar) <(baz | quux)

Анонимные именованные каналы управляются bash, поэтому они создаются и уничтожаются автоматически (в отличие от временных файлов).

Намного более подробно, чем моя редакция того же решения - анонимной партии -. +1

VonC 06.12.2008 13:38

В Bash это называется замена процесса.

Franklin Yu 14.04.2016 07:51

Некоторые люди, попадающие на эту страницу, могут искать построчное сравнение, для которого вместо этого следует использовать comm или grep -f.

Следует отметить, что во всех примерах ответов различие фактически не запускается, пока оба потока не закончатся. Проверьте это, например:

comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)

Если это проблема, вы можете попробовать SD (stream diff), который не требует сортировки (как это делает comm) и подстановки процессов, как в приведенных выше примерах, на порядки или величины быстрее, чем grep -f, и поддерживает бесконечные потоки.

Предлагаемый мной тестовый пример на sd был бы записан так:

seq 100 | sd 'seq 10 20 && sleep 5 && seq 20 30'

Но разница в том, что seq 100 сразу будет отличаться от seq 10. Обратите внимание, что если один из потоков - это tail -f, различие не может быть выполнено с заменой процесса.

Вот Сообщение блога, который я написал о различиях потоков на терминале, который представляет sd.

Другие вопросы по теме