Различный вывод двух программ без временных файлов

Скажем, у меня тоже есть программы a и b, которые я могу запускать с ./a и ./b.

Можно ли сравнивать их результаты без предварительной записи во временные файлы?


person Verhogen    schedule 26.09.2010    source источник


Ответы (6)


Используйте <(command) для передачи вывода одной команды в другую программу, как если бы это было имя файла. Bash передает вывод программы в канал и передает имя файла, например /dev/fd/63, внешней команде.

diff <(./a) <(./b)

Точно так же вы можете использовать >(command), если хотите передать что-то в команду.

На странице руководства Bash это называется «подстановкой процесса».

person John Kugelman    schedule 26.09.2010
comment
Один недостаток, о котором следует помнить, заключается в том, что если ./a или ./b не сработают, вызывающий не узнает об этом. - person Alexander Pogrebnyak; 27.09.2010
comment
OP пометил вопрос bash, но, к сведению, это не работает ни в одной другой оболочке. Это расширение bash к стандарту служебных программ Posix. - person DigitalRoss; 27.09.2010
comment
Я попробовал это решение с помощью программы на Java и получил следующую ошибку: -bash: syntax error near unexpected token ('. Я попробовал еще раз без скобок и получил -bash: java: No such file or directory. Не работает, если у команды есть параметры? - person styfle; 27.10.2011
comment
@DigitalRoss - решение можно распространить на другие оболочки с помощью псевдонима. В tcsh работает следующее уродство: alias diffcmd bash -c \'diff \<\(sh -c \!:1\) \<\( sh -c \!:2 \)\'. (Тогда, например, diffcmd ls ls -a). - person Paul Lynch; 15.05.2013
comment
Для тех, кто заходит за пределы Google, это также работает под zsh. (Кроме того, если вам нужно передать ввод во что-то, что вызывает fseek, zsh предлагает =(./a), который может использоваться идентично <(./a), но использует временный файл под капотом, который zsh удалит за вас.) - person ssokolow; 24.01.2017

Добавляя к обоим ответам, если вы хотите увидеть параллельное сравнение, используйте vimdiff:

vimdiff <(./a) <(./b)

Что-то вроде этого:

введите описание изображения здесь

person brokenfoot    schedule 26.03.2014
comment
vimdiff создает красивые, умные и интерактивные представления для сравнения различий. Похоже, что в большинстве систем он поставляется с пакетом vim. - person Tim Visée; 06.02.2017
comment
vimdiff также показывает не только отличающуюся строку, но и конкретный фрагмент текста, который отличается. - person Anton Tarasenko; 27.11.2018

Один из вариантов - использовать именованные каналы (FIFO):

mkfifo a_fifo b_fifo
./a > a_fifo &
./b > b_fifo &
diff a_fifo b_fifo

... но решение Джона Кугельмана намного чище.

person martin clayton    schedule 26.09.2010
comment
Лучше удалить именованные каналы после их использования с rm a_fifo b_fifo. - person Franklin Yu; 14.04.2016

Для всех, кому интересно, вот как вы выполняете замену процесса при использовании Fish shell:

Bash:

diff <(./a) <(./b)

Рыба:

diff (./a | psub) (./b | psub)

К сожалению, реализация в fish в настоящее время несовершенная; fish либо зависнет, либо будет использовать временный файл на диске. Вы также не можете использовать psub для вывода вашей команды.

person James McMahon    schedule 21.01.2016
comment
В настоящее время это не работает с рыбой. Если вывод программ больше одного BUFSIZ, команда зависнет. (или рыба просто использует временный файл на диске) - person Evan Benn; 16.07.2018

Добавляем еще немного к и без того хорошим ответам (мне помогло!):

Команда docker выводит свою справку в STD_ERR (т.е. файловый дескриптор 2)

Я хотел посмотреть, дают ли docker attach и docker attach --help одинаковый результат

$ docker attach

$ docker attach --help

Просто набрав эти две команды, я сделал следующее:

$ diff <(!-2 2>&1) <(!! 2>&1)

!! то же самое, что! -1, что означает запуск команды 1 перед этой - последней командой

! -2 означает запуск команды за две до этой

2> & 1 означает отправку вывода file_descriptor 2 (STD_ERR) в то же место, что и вывод file_descriptor 1 (STD_OUT)

Надеюсь, это было полезно.

person darrenthatcher    schedule 27.04.2015

Для zsh использование =(command) автоматически создает временный файл и заменяет =(command) на путь к самому файлу. При обычной подстановке процесса $(command) заменяется выводом команды.

Эта функция zsh очень полезна и может быть использована для сравнения вывода двух команд с помощью инструмента сравнения, например Beyond Compare:

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

Для Beyond Compare обратите внимание, что вы должны использовать bcomp для вышеуказанного (вместо bcompare), поскольку bcomp запускает сравнение и ожидает его завершения. Если вы используете bcompare, это запускает сравнение и немедленно завершает работу, из-за чего временные файлы, созданные для хранения вывода команд, исчезают.

Подробнее читайте здесь: http://zsh.sourceforge.net/Intro/intro_7.html

Также обратите внимание на это:

Обратите внимание, что оболочка создает временный файл и удаляет его по завершении команды.

и следующее, в чем разница между $(...) и =(...):

Если вы читаете справочную страницу zsh, вы можете заметить, что ‹(...) - это еще одна форма подстановки процесса, которая похожа на = (...). Между ними есть важное различие. В случае ‹(...) оболочка создает именованный канал (FIFO) вместо файла. Это лучше, так как это не заполняет файловую систему; но работает не во всех случаях. Фактически, если бы мы заменили = (...) на ‹(...) в приведенных выше примерах, все они перестали бы работать, кроме fgrep -f‹ (...). Вы не можете редактировать канал или открывать его как почтовую папку; Однако у fgrep нет проблем с чтением списка слов из канала. Вы можете спросить, почему diff ‹(foo) bar не работает, ведь foo | дифф - бар работает; это потому, что diff создает временный файл, если замечает, что один из его аргументов - -, а затем копирует свой стандартный ввод во временный файл.

person Ashutosh Jindal    schedule 29.06.2018