Труба на несколько файлов в оболочке


29

У меня есть приложение, которое будет производить большое количество данных, которые я не хочу хранить на диске. Приложение в основном выводит данные, которые я не хочу использовать, но набор полезной информации, которая должна быть разбита на отдельные файлы. Например, с учетом следующего вывода:

JUNK
JUNK
JUNK
JUNK
A 1
JUNK
B 5
C 1
JUNK

Я мог запустить приложение три раза так:

./app | grep A > A.out
./app | grep B > B.out
./app | grep C > C.out

Это даст мне то, что я хочу, но это займет слишком много времени. Я также не хочу записывать все выходные данные в один файл и анализировать его.

Есть ли способ объединить три операции, показанные выше, таким образом, что мне нужно всего лишь запустить приложение один раз и получить три отдельных выходных файла?

Ответы:


78

Если у вас есть тройник

./app | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null

( отсюда )

( о замене процесса )


4
Удивительно, это также может быть представлено как:./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
evilsoup

7
Этот ответ в настоящее время является единственно точным, учитывая первоначальное название вопроса «труба к нескольким процессам».
2013 г.

3
+1. Это наиболее общеприменимый ответ, поскольку он не зависит от того, какой была конкретная команда фильтрации grep.
Руах

1
Я бы согласился, что это лучший ответ на поставленный вопрос и должен быть отмечен так. Параллельно - еще одно решение (как было опубликовано), но проведя некоторые временные сравнения, приведенный выше пример более эффективен. Если вместо этого в операциях задействованы высокопроизводительные операции, такие как множественное сжатие файлов или множественное преобразование mp3, то, без сомнения, параллельное решение должно оказаться более эффективным.
AsymLabs

32

Вы можете использовать awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'

6
Заголовок вопроса - это труба к нескольким процессам , этот ответ о «конвейерной обработке» (отправке регулярным выражением) к нескольким файлам . Поскольку этот ответ был принят, название вопроса должно быть соответствующим образом изменено.
acelent

@PauloMadeira Вы правы. Как вы думаете, будет лучше название?
sj755

Я предложил очень маленькое редактирование «Передать несколько файлов в оболочке», оно ожидает пересмотра, зацените. Я ожидал удалить комментарий, если он был принят.
acelent

@PauloMadeira - я изменил название. Не видел ваших правок, но вы правы, использование процессов в заголовке было неправильным, если это принятый ответ.
slm

17

Вы также можете использовать возможности сопоставления с образцом вашей оболочки :

./app | while read line; do 
     [[ "$line" =~ A ]] && echo $line >> A.out; 
     [[ "$line" =~ B ]] && echo $line >> B.out; 
     [[ "$line" =~ C ]] && echo $line >> C.out; 
 done

Или даже:

./app | while read line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && echo $line >> "$foo".out; 
  done; done

Более безопасный способ справиться с обратными слешами и строками, начиная с -:

./app | while IFS= read -r line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && printf -- "$line\n" >> "$foo".out; 
  done; done

Как отмечает @StephaneChazelas в комментариях, это не очень эффективно. Лучшее решение, вероятно, @ AurélienOoms ' .


Это предполагает , что входные данные не содержат символы или пробелы или подстановочные символы или строки , которые начинаются с -n, -e... Это также будет очень неэффективной , так как это означает , что несколько системных вызовов для каждой строки ( по одному read(2)на символ, файл будучи открытым, писать закрыто для каждой строки ...). Как правило, использование while readциклов для обработки текста в оболочках является плохой практикой.
Стефан Шазелас

@ StephaneChazelas Я отредактировал свой ответ. Теперь он должен работать с обратным слешем и -nт. Д. Насколько я могу сказать, обе версии работают нормально с пробелами, правда, я не прав?
Terdon

Нет, первым аргументом printfявляется формат. Там нет причин оставлять вам переменные без кавычек там.
Стефан Шазелас

Это также приведет к разрыву в bash (и других оболочках, которые используют cstrings аналогичным образом), если во входных данных есть нули.
Крис Даун

9

Если у вас несколько ядер и вы хотите, чтобы процессы были параллельными, вы можете сделать:

parallel -j 3 -- './app | grep A > A.out' './app | grep B > B.out' './app | grep C > C.out'

Это породит три процесса в параллельных ядрах. Если вы хотите, чтобы какой-то вывод выводился на консоль или мастер-файл, он имеет преимущество в том, что вывод выводится в некотором порядке, а не в микшировании.

Утилиту gnu параллельно с Ole Tange можно получить из большинства репозиториев под названием Parallel или moreutils . Источник может быть получен от Savannah.gnu.org . Также вводное учебное видео здесь .

добавление

Используя более свежую версию параллели (не обязательно версию в вашем репозитории), вы можете использовать более элегантную конструкцию:

./app | parallel -j3 -k --pipe 'grep {1} >> {1}.log' ::: 'A' 'B' 'C'

Это позволяет получить результат запуска одного ./app и трех параллельных процессов grep в отдельных ядрах или потоках (как определено самой параллелью, также следует учитывать, что параметр -j3 является необязательным, но он приведен в этом примере для ознакомительных целей).

Более новую версию параллели можно получить, выполнив:

wget http://ftpmirror.gnu.org/parallel/parallel-20131022.tar.bz2

Затем обычная распаковка, cd to parallel- {date}, ./configure && make, sudo make install. Это установит параллель, страницу руководства параллель и страницу руководства параллельно.


7

Вот один в Perl:

./app | perl -ne 'BEGIN {open(FDA, ">A.out") and 
                         open(FDB, ">B.out") and 
                         open(FDC, ">C.out") or die("Cannot open files: $!\n")} 
                  print FDA $_ if /A/; print FDB $_ if /B/; print FDC $_ if /C/'

1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.