Трубопровід до декількох файлів у оболонці


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
Ця відповідь наразі є єдиною точною, враховуючи оригінальну назву питання "передача декількох процесів".
ацелент

3
+1. Це найбільш загальноприйнятна відповідь, оскільки це не залежить від того, якою була конкретна команда фільтрації grep.
ruakh

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

32

Можна використовувати awk

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

6
Назва запитання - це "множина процесів". Ця відповідь стосується "трубопроводу" (диспетчеризації за допомогою регулярного вираження) до декількох файлів . Оскільки ця відповідь була прийнята, назву питання слід змінити відповідно.
ацелент

@PauloMadeira Ви маєте рацію. Як ви думаєте, що було б кращим титулом?
sj755

Я запропонував дуже невелике редагування "Труба до декількох файлів в оболонці", це очікує на перегляд, перевірте це. Я очікував видалити коментар, якщо він буде прийнятий.
ацелент

@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, може бути отримана від більшості репостів під назвою паралельні або moreutils . Джерело можна отримати на Savannah.gnu.org . Також вступне навчальне відео тут .

Додаток

Використовуючи більш нову версію паралелі (не обов'язково версію у вашому репортажі репортажу), ви можете використовувати більш елегантну конструкцію:

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

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

Більш нову версію паралелі можна отримати, виконавши:

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

Тоді звичайний розпакувати, cd до паралельного - {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.