Как вы можете разделить два трубопровода в Bash?
Как вы можете diff два конвейера без использования временных файлов в 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
, чтобы сделать это без использования временных файлов?
Ответы
Ответ 1
Однострочный файл с 2 tmp (не то, что вы хотите):
foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt
С bash вы можете попробовать:
diff <(foo | bar) <(baz | quux)
Как уже упоминалось в подробном ответе BenM, <
создает анонимные именованные каналы - управляется bash - поэтому они автоматически создаются и уничтожаются, в отличие от временные файлы.
Тем не менее, Даниэль Кэссиди указывает, что "без использования временных файлов" часть вопроса не соблюдается: файловая система все еще изменена (с записью каталога, обозначающей именованный канал создано, а затем удалено)
В противном случае, как вы упомянули в своем вопросе, вам нужно использовать - как STDIN
foo | bar > file1.txt && baz | quux | diff file1.txt - && rm file1.txt
поскольку, как представляется, нет простого способа передать несколько входов одной команде.
Вы можете передавать только один вывод на несколько входов с помощью команды tee:
ls *.txt | tee /dev/tty txtlist.txt
Вышеуказанная команда выводит вывод ls *.txt на терминал и выводит его в текстовый файл txtlist.txt.
Ответ 2
В bash вы можете использовать подоболочки для индивидуального выполнения конвейеров команд, заключая конвейер в круглые скобки. Затем вы можете префикс их с помощью < для создания анонимных именованных каналов, которые затем можно передать в diff.
Например:
diff <(foo | bar) <(baz | quux)
Анонимные именованные каналы управляются bash, поэтому они автоматически создаются и уничтожаются (в отличие от временных файлов).
Ответ 3
Некоторые люди, приходящие на эту страницу, могут искать линейный diff, для которых вместо этого следует использовать comm
или grep -f
.
Следует отметить, что во всех примерах ответов разницы на самом деле не начнутся, пока оба потока не закончатся. Проверьте это, например:
comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)
Если это проблема, вы можете попробовать sd (поток diff), который не требует сортировки (например, comm
) или замещение процесса, как в приведенных выше примерах, порядка или величины быстрее, чем grep -f
и поддерживает бесконечные потоки.
Пример теста, который я предлагаю, будет написан следующим образом: sd
:
seq 100 | sd 'seq 10 20 && sleep 5 && seq 20 30'
Но разница в том, что seq 100
будет сразу отличаться от seq 10
. Обратите внимание, что если один из потоков является tail -f
, diff не может быть выполнен с заменой процесса.
Здесь blogpost Я написал о различных потоках на терминале, который вводит sd
.