grep с двумя или более словами, одна строка за файлом со многими файлами - PullRequest
0 голосов
/ 24 февраля 2019

все.У меня есть

file 1.log: text1 value11 text text text text2 value12 text

file 2.log: text1 value21 text text text text2 value22 text

Я хочу:

value11;value12 value21;value22

На данный момент я grep значения вразделите файлы и вставьте их позже в другой файл, но я думаю, что это не очень элегантное решение, потому что мне нужно читать все файлы более одного раза, поэтому я пытаюсь использовать grep для извлечения всех данных в одной папке |grep line, но это не тот результат, который я ожидал.

Я использую: cat *.log | grep -oP "(?<=text1 ).*?(?= )|(?<=text2 ).*?(?= )" | tr '\n' '; '

или

cat *.log | grep -oP "(?<=text1 ).*?(?= )|(?<=text2 ).*?(?= )" | xargs

но я получаю в каждомcase:

value11;value12;value21;value22

value11 value12 value21 value22

Большое спасибо.

Ответы [ 3 ]

0 голосов
/ 24 февраля 2019

Я согласен с @ John1024 , и то, как вы решите эту проблему, будет зависеть от того, какой именно текст вы ищете.Если, например, ваши опасения начинаются с text{1,2,...}, и тогда то, что вы хотите во втором поле, может быть чем угодно, тогда его подход оптимален.Однако, если значения в первом поле изменяются и что вас действительно интересует, это записи, в которых у вас есть valueXX во втором поле, то подход, отключающий второе поле, может быть тем, что вы ищете.

Если взять, например, ваше второе поле, если интересующий вас текст имеет форму valueXX (где XX - это две или более цифры в конце поля), вы можете обрабатывать только те записи, в которых вашвторое поле совпадает, а затем используется простое условное тестирование: FNR == 1 для управления выводом ';' разделителя и ENDFILE для управления новой строкой, аналогичной:

awk '$2 ~ /^value[0-9][0-9][0-9]*$/ {
    printf "%s%s", (FNR == 1) ? "" : ";", $2
}
ENDFILE {
    print ""
}' file1.log file2.log

Пример использования / вывода

$ awk '$2 ~ /^value[0-9][0-9][0-9]*$/ {
    printf "%s%s", (FNR == 1) ? "" : ";", $2
}
ENDFILE {
    print ""
}' file1.log file2.log
value11;value12
value21;value22

Посмотрите вещи и рассмотрите ваши фактические входные файлы, и тогда любой из этих двух подходов приведет вас туда.

0 голосов
/ 24 февраля 2019

Если я вас правильно понял, вы хотите values, но ищите text[12], т.е.чтобы получить слово после совпадения поискового слова, а не совпадающее поисковое слово:

$ awk -v s="^text[12]$" '                   # set the search regex *
FNR==1 {                                    # in the beginning of each file
    b=b (b==""?"":"\n")                     # terminate current buffer with a newline
}
{
    for(i=1;i<NF;i++)                       # iterate all but last word
        if($i~s)                            # if current word matches search pattern
            b=b (b~/^$|\n$/?"":";") $(i+1)  # add following word to buffer
}
END {                                       # after searching all files
    print b                                 # output buffer
}' *.log

Вывод:

value11;value12
value21;value22

* регулярное выражение также может быть, например, ^(text1|text2)$.

0 голосов
/ 24 февраля 2019

Попробуйте:

$ awk -v RS='[[:space:]]+' '$0=="text1" || $0=="text2"{getline; printf "%s%s",sep,$0; sep=";"} ENDFILE{if(sep)print""; sep=""}' *.log
value11;value12
value21;value22

Для тех, кто предпочитает, чтобы их команды распределялись по нескольким строкам:

awk -v RS='[[:space:]]+' '
    $0=="text1" || $0=="text2" {
        getline
        printf "%s%s",sep,$0
        sep=";"
     }
     ENDFILE {
        if(sep)print""
        sep=""
     }' *.log

Как это работает

  • -v RS='[[:space:]]+'

    Это говорит awk о необходимости рассматривать любую последовательность пробелов (переводы строк, пробелы, табуляции и т. Д.) Как разделитель записей.

  • $0=="text1" || $0=="text2"{getline; printf "%s%s",sep,$0; sep=";"}

    Это говорит awk искать записи файла, которые соответствуют либо text1 or text2`.Для этих записей и этих записей выполняются только команды в фигурных скобках.Эти команды:

    • getline говорит awk прочитать в следующей записи.

    • printf "%s%s",sep,$0 говорит awk распечатать переменную sep, за которым следует слово в записи.

    • После того, как мы напечатаем первое совпадение, выполняется команда sep=";", которая сообщает awk для установки значения sep на точку с запятой.

      Когда мы запускаем каждый файл, sep пуст.Это означает, что первое совпадение из любого файла печатается без разделителя перед ним.Все последующие совпадения из одного и того же файла будут иметь ; для их разделения.

  • ENDFILE{if(sep)print""; sep=""}

    После окончания каждого файлапо достижении, мы печатаем новую строку, если sep не пусто, а затем устанавливаем sep обратно в пустую строку.

Альтернатива: печать второго слова, если первое слово заканчиваетсяс номером

В альтернативной интерпретации вопроса (шапка: Дэвид С. Ранкин ) мы хотим напечатать второе слово в любой строке, для которой первое слово заканчивается начисло.В этом случае попробуйте:

$ awk '$1~/[0-9]$/{printf "%s%s",sep,$2; sep=";"} ENDFILE{if(sep)print""; sep=""}' *.log
value11;value12
value21;value22

. В приведенном выше примере $1~/[0-9]$/ выбирает строки, для которых первое слово заканчивается цифрой, а printf "%s%s",sep,$2 печатает второе поле в этой строке.

Обсуждение

Первоначальная команда была:

$ cat *.log | grep -oP "(?<=text1 ).*?(?= )|(?<=text2 ).*?(?= )" | tr '\n' '; '
value11;value12;value21;value22;

Обратите внимание, что при использовании большинства команд Unix cat редко когда-либо требуется.В этом случае, например, grep принимает список файлов.Таким образом, мы могли бы легко обойтись без дополнительного cat процесса и получить такой же результат:

$ grep -hoP "(?<=text1 ).*?(?= )|(?<=text2 ).*?(?= )" *.log | tr '\n' '; '
value11;value12;value21;value22;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...