Могу ли я объединить несколько команд и сделать так, чтобы все они принимали один и тот же ввод из стандартного ввода? - PullRequest
2 голосов
/ 12 июня 2009

В bash есть ли способ объединить несколько команд, все из которых принимают один и тот же ввод из stdin? То есть одна команда читает stdin, выполняет некоторую обработку, записывает вывод в файл. Следующая команда в цепочке получает тот же ввод, что и первая команда. И так далее.

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

cat food_expenses.txt | grep "coffee" > coffee.txt | grep "tea" > tea.txt | grep "honey cake" > cake.txt

Это, очевидно, не работает, потому что второй grep получает вывод первого grep , а не исходный текстовый файл. Я попытался вставить тройник , но это не помогает. Есть ли какая-то магия bash, которая может заставить первую команду grep отправлять свой вход в канал, а не на выход?

И, кстати, разделение файла было простым примером. Рассмотрите возможность разделения (подбора файлов путем поиска по шаблону) непрерывного потока живого текста, поступающего по сети и записывающего выходные данные в разные именованные каналы или сокеты. Я хотел бы знать, если есть простой способ сделать это с помощью сценария оболочки.

(Этот вопрос - исправленная версия моей более ранней , основанная на ответах, которые указали на неясность)

Ответы [ 8 ]

10 голосов
/ 24 сентября 2009

Для этого примера вы должны использовать awk, как предлагает полу бесполезный.

Но в целом, чтобы N произвольных программ считывали копию одного входного потока, вы можете использовать tee и оператор подстановки выходных данных процесса bash:

tee <food_expenses.txt \
  >(grep "coffee" >coffee.txt) \
  >(grep "tea" >tea.txt) \
  >(grep "honey cake" >cake.txt)

Обратите внимание, что >(command) является расширением bash.

5 голосов
/ 12 июня 2009

Очевидный вопрос: почему вы хотите сделать это в одной команде?

Если вы не хотите писать скрипт и хотите запускать вещи параллельно, bash поддерживает концепции подоболочек , и они могут выполняться параллельно. Поместив команду в квадратные скобки, вы можете запускать greps (или что-то еще) одновременно, например,

$ (grep coffee food_expenses.txt > coffee.txt) && (grep tea food_expenses.txt > tea.txt) 

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

Вы можете (вместо этого) поиграть с перенаправлением вывода через разные потоки. Вы не ограничены stdout / stderr, но можете назначать новые потоки по мере необходимости. Я не могу посоветовать больше об этом, кроме как направить вас к примерам здесь

2 голосов
/ 12 июня 2009

Мне нравится Идея Стивена об использовании awk вместо grep.

Это не красиво, но вот команда, которая использует перенаправление вывода, чтобы все данные проходили через stdout:

cat food.txt | 
awk '/coffee/ {print $0 > "/dev/stderr"} {print $0}' 
    2> coffee.txt | 
awk '/tea/ {print $0 > "/dev/stderr"} {print $0}' 
    2> tea.txt

Как видите, он использует awk для отправки всех строк, соответствующих 'coffee', на stderr и всех строк независимо от содержимого на stdout. Затем stderr подается в файл, и процесс повторяется с «чаем».

Если вы хотите отфильтровать контент на каждом шаге, вы можете использовать это:

cat food.txt | 
awk '/coffee/ {print $0 > "/dev/stderr"} $0 !~ /coffee/ {print $0}' 
    2> coffee.txt | 
awk '/tea/ {print $0 > "/dev/stderr"} $0 !~ /tea/ {print $0}' 
    2> tea.txt
1 голос
/ 24 июня 2009

Мне непонятно, почему фильтрация должна выполняться разными шагами. Одна awk-программа может сканировать все входящие строки и отправлять соответствующие строки в отдельные файлы. Это очень простая диспетчеризация, которая может подавать несколько вторичных команд (то есть постоянные процессы, которые контролируют выходные файлы для нового ввода, или файлы могут быть сокетами, которые установлены заранее и записаны процессом awk.)

Если есть причина, чтобы каждый фильтр видел каждую строку, просто удалите «next»; заявления, и каждый фильтр будет видеть каждую строку.

$ cat split.awk
BEGIN{}
/^coffee/ {
    print $0 >> "/tmp/coffee.txt" ;
    next;
}
/^tea/ {
    print $0 >> "/tmp/tea.txt" ;
    next;
}
{ # default
    print $0 >> "/tmp/other.txt" ;
}
END {}
$
1 голос
/ 12 июня 2009

Вот два bash скрипта без awk. Второй даже не использует grep!

С grep:

#!/bin/bash
tail -F food_expenses.txt | \
while read line
do
    for word in "coffee" "tea" "honey cake"
    do
        if [[ $line != ${line#*$word*} ]]
        then
            echo "$line"|grep "$word" >> ${word#* }.txt # use the last word in $word for the filename (i.e. cake.txt for "honey cake")
        fi
    done
done

без grep:

#!/bin/bash
tail -F food_expenses.txt | \
while read line
do
    for word in "coffee" "tea" "honey cake"
    do
        if [[ $line != ${line#*$word*} ]] # does the line contain the word?
        then
            echo "$line" >> ${word#* }.txt # use the last word in $word for the filename (i.e. cake.txt for "honey cake")
        fi
    done
done;

Edit:

Вот метод AWK:

awk 'BEGIN {
         list = "coffee tea"; 
         split(list, patterns)
     }
     {
         for (pattern in patterns) {
             if ($0 ~ patterns[pattern]) {
                 print > patterns[pattern] ".txt"
             }
         }
     }' food_expenses.txt

Работа с шаблонами, включающими пробелы, еще не решена.

1 голос
/ 12 июня 2009

Вы можете использовать awk, чтобы разбить до двух файлов:

awk '/Coffee/ { print "Coffee" } /Tea/ { print "Tea" > "/dev/stderr" }' inputfile > coffee.file.txt 2> tea.file.txt
0 голосов
/ 12 июня 2009

Предполагая, что ваши входные данные не бесконечны (как в случае сетевого потока, который вы никогда не планируете закрывать), я мог бы рассмотреть возможность использования подоболочки для помещения данных во временный файл, а затем ряда других подоболочек для чтения Это. Я не проверял это, но, возможно, это будет выглядеть примерно так {cat inputtream> tempfile}; {grep tea tempfile> tea.txt}; {grep coffee tempfile> coffee.txt};

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

0 голосов
/ 12 июня 2009

Вероятно, вы можете написать простой скрипт на AWK, чтобы сделать это за один раз. Можете ли вы описать формат вашего файла немного больше?

  • Это пробел / запятая?
  • есть ли у вас описания элементов в определенном «столбце», где столбцы определяются некоторым разделителем, таким как пробел, запятая или что-то еще?

Если вы можете позволить себе несколько прогонов grep, это будет работать,

grep coffee food_expanses.txt> coffee.txt
grep tea food_expanses.txt> tea.txt

и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...