2008-12-05 2 views
110

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

foo | bar 
baz | quux 

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

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

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

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

ответ

112

однострочный с 2 ​​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 

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


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

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

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

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

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

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

Обратите внимание, что вы можете только труба один выход к несколько входов с командой тройник :

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

Вышеприведенные команда отображает выход Ls * .txt к терминалу и выводит его в текстовый файл txtlist.txt.

Но с подменой процесса, вы можете использовать tee кормить одни и те же данные в несколько трубопроводов:

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

Даже без bash вы можете использовать временные fifo's `mkfifo a; cmd> a & cmd2 | diff a -; rm a` – unhammer 2013-06-10 10:49:47

+0

Вы можете использовать регулярный канал для одного из аргументов: `pip1 | diff -u - <(конвейер2) `. Тогда вывод будет более четко напоминать вам, какой вход был, который, показывая `-/dev/stdin` против` ++/dev/fd/67` или что-то, вместо двух пронумерованных fds. – 2018-03-05 04:36:33

106

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

Например:

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

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

3

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

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

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

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

Испытание примера я предлагаю было бы написано, как это в sd:

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

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

Вот blogpost Я писал о различных потоках на терминале, в котором вводится sd.