Я пытаюсь получить только совпадающие строки (match_E2 и pattern_2) вместе с 1-м столбцом.

abcd.corp;;a123,Virtual,aws,Linux,Linux,match_E2,Database
web1.corp;;,Virtual,azure,match_E2,Linux,corpo,Database
web2.corp;;match_E2,Virtual,a2responsible,Linux_Suse,Linux,corpo,Database
web3.corp;;Virtual,Virtual,corpo,pattern_2,Linux,corpo,Database
web4.corp;;Virtual,Virtual,corpo,,Linux,pattern_2,Database

Ожидаемый результат может быть ниже

abcd.corp,match_E2
web1.corp,match_E2
web2.corp,match_E2
web3.corp,pattern_2
web4.corp,pattern_2

Я попытался использовать параметр -o в grep, но он дает только совпадающие строки.

2
AdminBee 20 Авг 2020 в 14:55
2
Это должно быть grep или awk тоже вариант? Ваши теги вопросов, похоже, указывают на это, просто хочу убедиться. Кроме того, как именно определяются шаблоны. Простые строки или регулярные выражения? Всегда ли они одинаковы для одного запуска обработки, или вы ищете несколько совпадений, которые можно найти за один проход? Кроме того, действительно ли у вас есть смешанные разделители (; и ,), и если да, может ли совпадение происходить только в части, разделенной ,?
 – 
AdminBee
20 Авг 2020 в 13:47
Подойдет что угодно, либо awk, либо grep, либо оба, но больше я предпочитаю однострочную команду, шаблон аналогичен тому, что я упоминал
 – 
Sin15
20 Авг 2020 в 13:53
Проблема в том, что вы упомянули два шаблона. Хотите ли вы искать оба варианта за один запуск искомой команды или можно вызывать команду дважды (т. е. один раз для каждого шаблона поиска). Кроме того, гарантируется ли, что шаблон может встречаться только один раз в каждой строке, или может быть несколько вхождений в одной строке?
 – 
AdminBee
20 Авг 2020 в 13:55
Да, шаблон встречается только один раз в каждой строке либо match_E2, либо pattern_2, либо ни одного. Но не оба одновременно.
 – 
Sin15
20 Авг 2020 в 14:00
1
mismatch_E2 соответствует match_E2? Как насчет pattern_275 соответствия pattern_2? Вы сказали, что хотите сопоставить 2 строки, но приняли ответ, который соответствует 2 частичным регулярным выражениям, просто подтвердив, что частичное совпадение регулярных выражений действительно является тем, что вам нужно, а не полное соответствие строки.
 – 
Ed Morton
21 Авг 2020 в 02:24

6 ответов

Лучший ответ

Осмелюсь предположить, что ваше дело может быть лучше решено с помощью sed.

Для шаблона match_E2:

$ sed -nE 's/^([^;]+).*(match_E2).*/\1,\2/p' file.txt

Для шаблона pattern_2:

$ sed -nE 's/^([^;]+).*(pattern_2).*/\1,\2/p' file.txt

Для обоих этих шаблонов за один раз:

$ sed -nE 's/^([^;]+).*(match_E2|pattern_2).*/\1,\2/p' file.txt

То есть в основном:

$ sed -nE 's/^([^;]+).*(    ).*/\1,\2/p' file.txt
#                       ^  ^
#                       |  |
#            ---------------------
# put within these two parentheses the same (Extended Regular Expression) pattern you would use with `grep -E`

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

5
LL3 20 Авг 2020 в 14:38

Следующая команда awk должна делать то, что вы хотите:

awk -F'[;,]' -v pat="match_E2" '$0~pat{for (i=3;i<NF;i++) {if ($i ~ pat) printf("%s,%s\n",$1,$i)}}' file.txt
  • Параметр -F'[;,]' указывает awk распознавать как ;, так и , в качестве разделителя полей и соответствующим образом разделять строку. Обратите внимание: хотя стандарт POSIX предписывает, чтобы такой многосимвольный разделитель полей интерпретировался как полное регулярное выражение, все еще могут существовать awk версии, которые не реализуют это правильно.

  • Шаблон передается в awk через параметр командной строки -v pat="match_E2". Обратите внимание, что при этом шаблон интерпретируется как полное регулярное выражение. Если у вас есть символы, которые имеют особое значение в этом контексте, вам нужно их экранировать!

  • Если текущая строка соответствует шаблону ($0 ~ pat означает "если вся строка соответствует регулярному выражению, хранящемуся где-то в pat"), она будет перебирать все соответствующие поля (поле 3 — первое поле после поля pat). last ;) и определить тот, который действительно совпал (условие if ($i ~ pat)). Затем он печатает первое поле ($1) и соответствующее поле ($i) через printf(). Это предполагает, что в соответствующей строке может быть только одно такое поле!

Если вы ищете несколько шаблонов, вы можете соответствующим образом сформулировать регулярное выражение в pat, как в

awk -F'[;,]' -v pat="match_E2|pattern_2" ' ... etc ... '

Или выполните команду дважды, по одному разу для каждого шаблона.

4
AdminBee 20 Авг 2020 в 14:18
Спасибо @AdminBee, все работает как положено. Не могли бы вы объяснить строку - '$0~pat{for (i=2;i<NF;i++) {if ($i ~ pat) printf("%s,%s\n",$1,$i)}} '
 – 
Sin15
20 Авг 2020 в 14:12
2
Я отредактировал ответ, чтобы включить больше объяснений. Вы можете обратиться к руководству пользователя GNU awk для получения дополнительной информации. если ты заинтересован.
 – 
AdminBee
20 Авг 2020 в 14:18

Несколько более похожая на grep -o версия awk, использующая функцию match:

$ awk -F';' 'match($0,/match_E2|pattern_2/) {print $1 "," substr($0,RSTART,RLENGTH)}' file
abcd.corp,match_E2
web1.corp,match_E2
web2.corp,match_E2
web3.corp,pattern_2
web4.corp,pattern_2
4
steeldriver 20 Авг 2020 в 16:33
Не могли бы вы объяснить вашу команду ..
 – 
Sin15
21 Авг 2020 в 07:57

Лучшее, что я могу предложить, это очень ОЧЕНЬ уродливый обходной путь:

# Finds first columns
first=(`grep "match_E2" text | awk -F';;' '{print $1}'`)

# Lets join them together now
for column in ${first[@]}; do
  echo "$column,match_E2"
done

Результат:

abcd.corp,match_E2
web1.corp,match_E2
web2.corp,match_E2

Вы также можете создать сценарий или функцию и заменить строку поиска на $1, а затем вызвать ее со строкой поиска в качестве аргумента. Нравится:

Сценарий:

#!/bin/bash
first=(`grep "$1" text | awk -F';;' '{print $1}'`)
for column in ${first[@]}; do
  echo "$column,$1"
done

И тогда вы называете это так:

xxxx@ubuntu:~/test# ./script.sh match_E2
abcd.corp,match_E2
web1.corp,match_E2
web2.corp,match_E2
1
pormulsys 20 Авг 2020 в 13:51
1
Спасибо @pormulsys, я ищу однострочную команду либо awk/grep/или комбинацию обоих. кстати, есть две совпадающие строки (match_E2 и pattern_2), как указано в ожидаемом выводе.
 – 
Sin15
20 Авг 2020 в 13:59

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

$ awk '
    BEGIN { strs["match_E2"]; strs["pattern_2"]; FS=";"; OFS="," }
    { for (str in strs) if (index(","$NF",",","str",")) print $1, str }
' file
abcd.corp,match_E2
web1.corp,match_E2
web2.corp,match_E2
web3.corp,pattern_2
web4.corp,pattern_2

В качестве примера частичного и полного совпадения рассмотрим следующий ввод:

$ cat file
abcd.corp;;a123,Virtual,aws,Linux,Linux,mismatch_E2,Database
web1.corp;;,Virtual,azure,match_E2,Linux,corpo,Database
web2.corp;;match_E2,Virtual,a2responsible,Linux_Suse,Linux,corpo,Database
web3.corp;;Virtual,Virtual,corpo,pattern_275,Linux,corpo,Database
web4.corp;;Virtual,Virtual,corpo,,Linux,pattern_2,Database

Обратите внимание, что теперь первая строка ввода содержит mismatch_E2 вместо match_E2, а 4-я строка — pattern_275 вместо pattern_2. Теперь запустите на нем приведенный выше awk-скрипт и убедитесь, что он выдает ожидаемый результат:

$ awk '
    BEGIN { strs["match_E2"]; strs["pattern_2"]; FS=";"; OFS="," }
    { for (str in strs) if (index(","$NF",",","str",")) print $1, str }
' file
web1.corp,match_E2
web2.corp,match_E2
web4.corp,pattern_2

В качестве примера сопоставления регулярного выражения и строки измените match_E2 в строке 1 на m.*2 и patch_2 в 4-й строке на p.*2 во входных данных:

$ cat file
abcd.corp;;a123,Virtual,aws,Linux,Linux,m.*2,Database
web1.corp;;,Virtual,azure,m.*2,Linux,corpo,Database
web2.corp;;m.*2,Virtual,a2responsible,Linux_Suse,Linux,corpo,Database
web3.corp;;Virtual,Virtual,corpo,pattern_2,Linux,corpo,Database
web4.corp;;Virtual,Virtual,corpo,,Linux,pattern_2,Database

И измените приведенный выше awk-скрипт так, чтобы он искал m.*2 и p.*2 вместо match_E2 и pattern_2, и убедитесь, что он снова выдает ожидаемый результат:

$ awk '
    BEGIN { strs["m.*2"]; strs["p.*2"]; FS=";"; OFS="," }
    { for (str in strs) if (index(","$NF",",","str",")) print $1, str }
' file
abcd.corp,m.*2
web3.corp,p.*2
1
Ed Morton 21 Авг 2020 в 02:55
awk -F "," '{for(i=1;i<=NF;i++){if($i ~ /match_E2|pattern_2/){print $1,$i}}}'  filename|sed "s/;.*[ ;]/,/g"

Выход

abcd.corp,match_E2
web1.corp,match_E2
web2.corp,match_E2
web3.corp,pattern_2
web4.corp,pattern_2
0
Praveen Kumar BS 20 Авг 2020 в 21:33