как найти строку и неопределенное количество строк вверх и вниз для данной строки - PullRequest
2 голосов
/ 18 апреля 2020

Я использую grep, чтобы найти строку и неопределенное количество строк вверх и вниз по этой строке до совпадения с двойной линией разрыва. Например, в документе, где у меня есть что-то вроде этого:

Name: Alice
ID: 6969
Interests: foo,bar

Name: Bob
ID: 5555
Interests: foo,bar
Experience

Name: Carl
ID: 3236
Interests: foo,bar

Я хочу найти всю информацию о человеке по идентификатору, поэтому, если я ищу 5555, вывод, который я хочу получить:

Name: Bob
ID: 5555
Interests: foo,bar
Experience

Я пытался использовать grep -C n ID_string (где n - количество строк вверх и вниз, чтобы соответствовать заданной строке), но вывод - это фиксированное количество строк "n", и я хочу, чтобы до двойного разрыва линии. Есть идеи? Спасибо.

Ответы [ 6 ]

2 голосов
/ 18 апреля 2020

Использование любого awk в любой оболочке на каждом UNIX поле:

$ awk -v RS= -v ORS='\n\n' '/(^|\n)ID: 5555(\n|$)/' file
Name: Bob
ID: 5555
Interests: foo,bar
Experience

Несколько примеров использования этого же подхода для поиска других комбинаций значений из полей:

$ awk -v RS= -v ORS='\n\n' '/(^|\n)Name: [[:alpha:]]*[aA].*(\n|$)/' file
Name: Alice
ID: 6969
Interests: foo,bar

Name: Carl
ID: 3236
Interests: foo,bar

$ awk -v RS= -v ORS='\n\n' '/(^|\n)Name: [[:alpha:]]*[aA]/ && /(^|\n)ID: 6/' file
Name: Alice
ID: 6969
Interests: foo,bar

$ awk -v RS= -v ORS='\n\n' '/(^|\n)Name: [[:alpha:]]*[aA]/ && /(^|\n)ID: [0-9]+6(\n|$)/' file
Name: Carl
ID: 3236
Interests: foo,bar
2 голосов
/ 18 апреля 2020

Это может работать для вас (GNU sed):

sed -n '/\S/{h;:a;n;//{H;$!ba};g;/5555/p}' file

Отключить неявную печать -n.

Начать набор строк в области удержания, когда текущая строка содержит непробельный символ.

Завершить коллекцию пустой строкой или концом файла.

Проверить коллекцию на наличие требуемой строки и, если она совпадает, напечатать всю коллекцию. Повторите.


Чтобы добавить новую строку к результату, используйте:

sed -n '/\S/{h;:a;n;//{H;$!ba};z;H;g;/5555/p}' file

Sed - редактор потоков. Он используется для редактирования текстовых файлов и обычно обрабатывает строку текста одновременно. У него есть два буфера, которые он использует для выполнения sh этой задачи. Пространство шаблона (PS) и резервный буфер, называемый пространством удержания (HS). Обычный поток событий заключается в том, что sed читает строку текста в PS и удаляет ее новую строку. Инструкции (команды) Sed действуют на PS, лишенный символ новой строки повторно добавляется, и результат доставляется на стандартный вывод, т.е. печатается.

Опция -n отключает имплицитный характер доставки PS на стандартный вывод. т. е. если вы хотите что-то напечатать, вы должны выполнить для этого команду, такую ​​как p или P, которая печатает PS или печатает первую строку PS.

Sed использует regexp для принятия решения если применять команды к PS. /\S/ - это регулярное выражение, которое проверяет PS на наличие любого непробельного символа. Sed использует парены для группировки команд, а команды разделяются точками с запятой.

Команда h заменяет все, что было в области удержания (HS), содержимым PS.

Sed может выполнять петли. Это делается путем определения метки-заполнителя для l oop и команды для перехода к метке-заполнителю l oop. :a определяет все oop местозаполнитель с именем a, а b - команда прерывания.

Команда n извлекает следующую строку в PS. Обычно это приводит к тому, что содержимое PS передается на стандартный вывод перед его заменой, но поскольку опция -n включена, ее содержимое просто выбрасывается.

Сокращение // для предыдущее регулярное выражение, т. е. содержимое PS теперь снова проверяется на наличие непробельного символа, и если это так, то выполняются команды внутри паренов. В этом случае H добавляет PS к HS, отделенному его новой строкой, которая была предварительно удалена.

Sed знает номер строки каждой строки, а также знает, когда в PS присутствует последняя строка файла. $ обозначает последнюю строку. ! является командой not и отменяет предыдущий адрес или регулярное выражение, например, $! означает не последнюю строку файла. Сложить все вместе $!ba означает, что если это не последняя строка файла, разбить b на заполнитель a. Таким образом, поток команд направляется обратно к :a и sed возобновляет обработку с этого места.

Если // не совпадает, это подразумевает две возможности: либо текущая строка пуста, либо это последняя строка файла. z убирает PS и очищает его. H добавляет пустую строку к HS, разделенную новой строкой.

g заменяет PS содержимым HS. Коллекция строк, которые построил l oop, теперь находится в PS. Другое регулярное выражение пытается сопоставить PS /5555/ и, если это так, выдает команду p, которая печатает PS.

Таким образом, программа sed перемещается по файлу, собирая коллекции непустых строк в HS и печатая их, если соответствует регулярное выражение.

1 голос
/ 18 апреля 2020

Не могли бы вы попробовать следующее.

awk '
/^Name/{
  if(found){
    print value
  }
  value=found=""
}
{
  value=(value?value ORS:"")$0
}
/ID:/{
  if($NF==5555){
    found=1
  }
}
END{
  if(found){
    print value
  }
}
'  Input_file

Объяснение: Добавление подробного объяснения приведенного выше кода здесь.

awk '                               ##Starting awk program from here.
/^Name/{                            ##Checking if a line starts with Name then do following.
  if(found){                        ##Checking if found is SET then do following.
    print value                     ##Printing variable value here.
  }
  value=found=""                    ##Nullifying value and found values here.
}
{
  value=(value?value ORS:"")$0      ##Creating value here which will have all lines value separated with new line.
}
/ID:/{                              ##Checking if a line has ID: then do following.
  if($NF==5555){                    ##Checking condition if last field is 5555.
    found=1                         ##Then set found=1 here.
  }
}
END{                                ##Starting END block of this program here.
  if(found){                        ##Checking if found is SET then do following.
    print value                     ##Printing variable value here.
  }
}
'  Input_file                       ##Mentioning Input_file name here.
0 голосов
/ 18 апреля 2020

Этот Perl однострочный будет делать:

what=5555 perl -00 -ne '/ID:\s+$ENV{what}/\m and print' file

Переключатель -00 включает режим paragraph. В этом режиме каждая запись представляет собой абзац (блок) текста, ограниченный одной или несколькими пустыми строками.

Абзац печатается, если строка внутри соответствует токену ID: с одним или несколькими конечными пробелами, после чего следует значением what, установленным при запуске скрипта. Обратите внимание, что what устанавливается только на время действия командной строки.

Модификатор регулярного выражения \m заставляет $ соответствовать концу каждой строки в строке. Это создает точные совпадения для what.

0 голосов
/ 18 апреля 2020
awk '
    !NF{delete buffer;i=0;go_on=0;next}
    /ID: 5555/{
        for(j=1;j<=i;j++)print buffer[j]
        go_on=1
    }
    go_on
    {i++;buffer[i]=$0}
' file

Если вы не хотите жестко кодировать ни идентификационный номер, ни имя файла, сохраните его и сделайте его исполняемым:

awk -v number=$1 '
    !NF{delete buffer;i=0;go_on=0;next}
    $0 ~ "ID: " number{
        for(j=1;j<=i;j++)print buffer[j]
        go_on=1
    }
    go_on
    {i++;buffer[i]=$0}
' "$2"

Затем вызовите его ./script 5555 file.


Пример входного файла:

Name: Alice
ID: 6969
Interests: foo,bar

Surname: John
Nickname: Bill
Name: Bob
ID: 5555
Interests: foo,bar
Experience

Name: Carl
ID: 3236
Interests: foo,bar

Вывод:

Surname: John
Nickname: Bill
Name: Bob
ID: 5555
Interests: foo,bar
Experience
0 голосов
/ 18 апреля 2020

Используя pcregrep, вы можете попробовать:

cat data.txt | pcregrep -M '(^.+$\n)*ID: 5555\n(^.+$\n)*'

С помощью небольшого твика вы также можете отделить возвращаемые / отображаемые совпадения с помощью новой строки для удобства чтения:

cat temp.txt | pcregrep -M '(^.+$\n)*ID: 5555\n(^.+$\n)*\n?'

Вы может потребоваться установить pcregrep, например, используя:

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