Сопоставить строку и вернуть предыдущую строку перед совпадением, содержащим шаблон - PullRequest
3 голосов
/ 17 апреля 2020

Я пишу сценарий bash, для которого в файле, содержащем несколько записей, где каждая запись имеет такую ​​структуру:

Id: 33239
Folder: /Contacts/Holder/Center
Date: 04/17/20 13:17
Revision: 34011
Attrs:
  firstName: Name
  lastName: First Second
  mobilePhone: +345555555
  fileAs: 2
  jobTitle: Médico
  company: some company
  email: test_1@somedomain.com

Мне нужно найти «Id» элемента, связанного с Speci c "Электронная почта". Для этого я пытаюсь использовать «сед» с трюмом. Но я не могу достичь своей цели. Это то, что я имею до сих пор, но я не получаю результатов, которые мне нужны.

id=$(grep $usuario -B20 /tmp/contactos \
    | grep "Folder: /Contacts/Holder" -B2 -A20 \
    | sed -n "/^Id: /h;/^  email: $usuario/{g;p;}" \
    | awk '{print $2}')

С этим я пытаюсь:

id= - присвоить значение переменной, которую я буду использовать позже в скрипте

$(grep $usuario -B20 /tmp/contactos - Получить все строки в файле, где появляется электронное письмо, а также получить 20 строк до него. Это связано с тем, что электронная почта связана с более чем одним Id неопределенным числом строк под самим идентификатором.

grep 'Folder: /Contacts/Holder' -B2 -A20 - я снова фильтрую, пытаясь теперь получить только результаты для идентификаторов для это письмо в указанном c «пути к папке».

sed -n '/^Id: /h;/^ email: $usuario/{g;p;} - эта часть не работает, и я не знаю, как это исправить. Здесь я пытаюсь вернуть строку, содержащую Id:, связанную с письмом. Что-то вроде: Id: 33239 в этом примере.

awk '{print $2}') - только я пытаюсь напечатать только число из этой строки (33239).

Может кто-нибудь помочь, пожалуйста, чтобы понять, как Я могу сделать это с помощью sed` или, если будет предоставлен любой другой вариант, это также будет приветствоваться:)

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

Ответы [ 4 ]

3 голосов
/ 17 апреля 2020
awk '$1=="email:" && $2=="test_1@somedomain.com"{print id} $1=="Id:"{id = $2}' input_file
  • разделитель полей по умолчанию разделяется на пробелы / табуляции / новые строки и удаляет начальные / конечные пробелы из содержимого поля
  • $1=="email:" проверяет, точно ли первое содержимое поля email: (это сравнение строк, а не регулярное выражение)
  • $1=="email:" && $2=="test_1@somedomain.com" если оба условия выполнены, выведите id variable
  • $1=="Id:"{id = $2}, при этом значение id сохраняется, когда первое поле равно Id:

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

3 голосов
/ 17 апреля 2020

Эта команда sed должна извлечь ее:

sed -n '
    /^Id: / {                 # If the line starts with "Id: "
        s///                  # Remove the "Id: "
        h                     # Store what is left in the hold space
    }
    /^  email: '"$email"'/ {  # If the line starts with "  email: " plus the email
        x                     # Swap pattern and hold space
        p                     # Print pattern space
        q                     # Stop processing
    }
' infile

, где $email - переменная оболочки, содержащая экранированную версию test_1@somedomain.com:

raw='test_1@somedomain.com'
email=$(sed 's|[]/.*^$\[]|\\&|g' <<< "$raw")

Это экранирует специальное значение sed символы .*/^$[]\.

Или, более компактно:

sed -n '/^Id: /{s///;h};/^  email: '"$email"'/{x;p;q}' infile

macOS sed требует дополнительного ; перед каждым закрытием }.

И да, это наверное проще с awk 101

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

На более широком рисунке вы пытаетесь связать воедино условия в папке и электронной почте, чтобы создать идентификатор. Так что awk - лучший выбор для решения всей проблемы. Если ваш Bash сценарий может подготовить сценарий ниже, вы можете вызвать его следующим образом:

id=$(awk -f /tmp/script.awk -v usario=test_1 /tmp/contactos)

Вот содержимое, которое ваш Bash скрипт должен записать в /tmp/script.awk:

/Id:/   { id=$2; folder="" }
/Folder:..Contacts.Holder/  { folder=$2 }
/email:/    { if (match($2, "^" usario "@") && folder != "") print id }

Вам следует остерегаться проблемы «соответствующего префикса». Примером является поиск "juan" без совпадения с "juanita". Вот почему сценарий использует функцию match() с регулярным выражением, которое оценивается как match($2, "^juan@"). Это будет точно соответствовать «juan@domain.com» без совпадения с «juanita@domain.com» или «somejuan@domain.com».

Примечание. Синтаксис awk объединяет строки и переменные, разделенные пробелами. Как говорится, «нужно привыкнуть». Вы можете добавить круглые скобки вокруг "^" usario "@", если это поможет ...

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

Вот два глупо чистых bash метода (без внешних утилит), общий метод, требующий постоянного числа имен и атрибутов полей, и (только для первого метода) относительно короткий входной файл:

  1. printf '%0.0s%s%0.0s %s%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s'\
        '%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s%0.0s'\
        '%0.0s%0.0s%0.0s%0.0s %s\n' $(<infile) | 
        while read Id Folder email; do 
            [[ $email == test_1@somedomain.com && 
               $Folder == /Contacts/Holder/Center ]] && 
            echo $Id
        done
    

    Как это работает: после printf то, что подается на while, выглядит так:

    33239 /Contacts/Holder/Center test_1@somedomain.com
    
  2. То же самое может быть сделано с помощью read много:

    while read a Id && read a Folder && read && read && read &&
          read && read && read && read && read && read && 
          read a email; do
          [[ $email == test_1@somedomain.com &&
             $Folder == /Contacts/Holder/Center ]] &&
          echo $Id
    done < infile
    
...