Как выбрать строку, ограниченную двумя разными строковыми шаблонами

Вопрос или проблема

У меня есть вывод, похожий на файл (имя файла whatever.com) ниже…

[...]~ # tmsh list sys file ssl-cert whatever.com_2024
sys file ssl-cert whatever.com_2024 {
    certificate-key-size 2048
    checksum SHA1:2520:ab40df7776dbbccb62345560511f2205d
    create-time 2024-08-12:19:34:07
    created-by x.y
    expiration-date 1754956799
    expiration-string "Aug 11 23:59:59 2025 GMT"
    fingerprint SHA256/D8:57:8E:8E:4A:1A:C3:3C:1B:6F:32:59:A7:36:66:92:6C
    issuer "CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1,O=DigiCert Inc,C=US"
    key-type rsa-public
    last-update-time 2024-08-12:19:34:07
    mode 33188
    revision 1
    serial-number 0a:1d:ca:c7:09:7b:49:59:b2
    size 2520
    source-path /var/run/key_mgmt/RvGubB/ssl.crt/whatever.com_2024
    subject "CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA"
    subject-alternative-name "DNS:whatever.com"
    updated-by x.y
    version 3

Я пытаюсь сгенерировать вывод команды на основе информации выше в строке 17 (начиная с subject)

cat whatever.com | awk 'BEGIN  {F=" "; FS = "\n"; RS = ""; OFS = "\n"} { print "openssl req -new -nodes -sha256 -newkey rsa:2048 -out "substr($1,RSTART+19,length($1)-25)"_2025.csr -keyout "substr($1,RSTART+19,length($1)-25)"_2025.key -subj "/"substr($17,RSTART+14,length($17)-14)"\""}'

Вывод выглядит нормально

openssl req -new -nodes -sha256 -newkey rsa:2048 -out whatever.com_2025.csr -keyout whatever.com_2025.key -subj "/CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA"

Однако он использует подстроку строки 1, которая может отличаться от строки 17, поэтому я хотел бы получить имя файла, а не использовать substr($1,RSTART+19,length($1)-24), чтобы оно было сгенерировано на основе строки $17 между “CN=” и “,O=”.

Также возможно ли, если строка 18 содержит строку “DNS”, изменить вывод, чтобы он выглядел вот так…

openssl req -new -nodes -sha256 -newkey rsa:2048 -out whatever.com_2025.csr -keyout whatever.com_2025.key -subj "/CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA" -addext "subjectAltName = DNS:whatever.com"

Спасибо за все быстрые ответы. Имена файлов должны генерироваться динамически из файла. Пример, включенный в документ, — это всего лишь небольшая часть большего вывода. Конфигурация BEGIN предназначена для создания этих секций, которые на этом этапе могут обрабатываться индивидуально по строкам.

Работа с нагрузочными балансировщиками F5. Они основаны на Linux, но не совсем построены как обычные серверы, что делает некоторые сценарии довольно сложными.

Я нашел решение с использованием split(), но теперь у меня возникла другая проблема.
Когда я выполняю команду отдельно, она, кажется, работает

 ~ # cat whatever.com | awk 'BEGIN  {F=" "; FS = "\n"; RS = ""; OFS = "\n"} {split($17,a,/,/); print substr(a[1],RSTART+17)}'
whatever.com

Однако, когда я добавляю её в оригинальный скрипт, она добавляет число “5” перед ним.

 ~ # cat whatever.com |  awk 'BEGIN  {F=" "; FS = "\n"; RS = ""; OFS = "\n"} { print "openssl req -new -nodes -sha256 -newkey rsa:2048 -out "split($17,a,/,/); print substr(a[1],RSTART+17)"_2025.csr -keyout "split($17,a,/,/); print substr(a[1],RSTART+17)"_2025.key -subj \"/"substr($17,RSTART+14,length($17)-14)"\""}' 

openssl req -new -nodes -sha256 -newkey rsa:2048 -out 5
whatever.com_2025.csr -keyout 5
whatever.com_2025.key -subj "/CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA"

Используйте match(string, regexp, arrayresult) из GNU awk с () в регулярном выражении, чтобы захватить нужную часть. Например

awk '
BEGIN  {F=" "; FS = "\n"; RS = ""; OFS = "\n"}
{ match($17, "CN=([^,]*)", x);
  cn = x[1]
  match($17, "\"(.*)\"", x)
  subject = x[1]
  if(match($18, "(DNS:.*)\"", x)>0){
   dns = x[1]
   toadd = " -addext \"subjectAltName = " dns "\""
  }
  print "openssl req -new -nodes -sha256 -newkey rsa:2048 -out " \
   cn "_2025.csr -keyout " \
   cn "_2025.key -subj \"/" \
   subject "\"" toadd
}'

Я, вероятно, просто обрабатывал бы строки по одной, как обычно, и смотрел на первое поле, разделенное пробелами, чтобы определить строки subject и alt-name. Затем вы можете разделить строку на другой массив, используя знаки препинания, и выбрать оттуда нужную часть. (Это при условии, что ничего нет перед частью CN — я не могу вспомнить, допускается ли это.) Если вы хотите действительно проверить окружающий текст (части CN= и DNS:), тогда я бы переключился на Perl, вместо этого тогда посмотрите ответ meuh.


awk '$1 == "subject" { subject=$2; split($2, a, "[=,]"); cn=a[2]; } 
     $1 == "subject-alternative-name" { split($2, a, "[\":]"); altname=a[3]; }
     END { out = "openssl ... -keyout " cn "_2025.key -subj "  subject; 
           if (altname) out = out " -altname \"subjectAltName = DNS:" altname "\"";
           print out 
     } '  < whatever.txt
openssl ... -keyout foo.whatever.com_2025.key -subj "CN=foo.whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA" -altname "subjectAltName = DNS:bar.whatever.com"

Чтобы ответить на вопрос автора вопроса о том, почему их код использует данные из строки 1… проверьте содержимое RSTART. Вы должны обнаружить RSTART=0, потому что никакие функции (например, match()) не были запущены, чтобы фактически заполнить RSTART.

Вместо попытки обработать весь вывод в виде абзаца, я бы посоветовал обрабатывать его как отдельные строки.

Другой подход awk:

$ cat whatever.com | awk -F'"' '            # установить двойные кавычки в качестве разделителя полей
{ gsub(/[[:space:]]/,"",$1)                 # удалить пробелы из 1-го поля
  if ($1=="subject")
     subject=$2                             # сохранить для дальнейшего использования
  else
  if ($1=="subject-alternative-name")
     print "openssl req -new -nodes -sha256 -newkey rsa:2048 -out whatever.com_2025.csr -keyout whatever.com_2025.key -subj \"/" subject "\" -addext \"subjectAltName = " $2 "\""
}'

Это генерирует:

openssl req -new -nodes -sha256 -newkey rsa:2048 -out whatever.com_2025.csr -keyout whatever.com_2025.key -subj "/CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA" -addext "subjectAltName = DNS:whatever.com"

Это предполагает, что вы можете обрабатывать строки одну за другой и не делать всех тех манипуляций с полями и записями, которые вы делали. Это также предполагает, что subject-alternative-name всегда присутствует:

# Захватим доменное имя в поле "subject", которое начинается с "CN=" и заканчивается запятой, и всё поле целиком
/ +subject .*CN=[^,]+,O=/ {
  match($2, /"(CN=([^,]+),O=[^"]+")/, arr);
  domain=arr[2];
}

# Печатаем только тогда, когда определим, присутствует ли ключевое слово "DNS" в поле "subject-alternative-name"
/ +subject-alternative-name / {
  # Если мы имеем ключевое слово "DNS", печатаем с "-addext", в противном случае печатаем без него
  if (match($2, /"(DNS:[^"]+)/, altarr))
    printf "openssl req -new -nodes -sha256 -newkey rsa:2048 -out %s_2025.csr -keyout %s_2025.key -subj \"/%s -addext \"subjectAltName = %s\"\n", domain, domain, arr[1], altarr[1];
  else
    printf "openssl req -new -nodes -sha256 -newkey rsa:2048 -out %s_2025.csr -keyout %s_2025.key -subj \"/%s\n", domain, domain, arr[1];
}

С ключевым словом DNS:

$ awk -f /tmp/UL_789631.awk < /tmp/UL_789631.txt 
openssl req -new -nodes -sha256 -newkey rsa:2048 -out whatever.com_2025.csr -keyout whatever.com_2025.key -subj "/CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA" -addext "subjectAltName = DNS:whatever.com"

Без ключевого слова DNS:

$ awk -f /tmp/UL_789631.awk < /tmp/UL_789631.txt 
openssl req -new -nodes -sha256 -newkey rsa:2048 -out whatever.com_2025.csr -keyout whatever.com_2025.key -subj "/CN=whatever.com,O=XYZ,L=Toronto,ST=Ontario,C=CA"

Это использует подвыражения регулярного выражения для захвата соответствующей части. Функция match выполняет сопоставление регулярного выражения и присваивает подвыражения (те, что в круглых скобках) массиву (здесь, arr для поля subject и altarr для поля subject-alternative-name). Мы используем два (перекрывающихся) подвыражения для subject: одно для значения поля и одно для доменного имени внутри значения поля.

Первая строка /.../ { соответствует полю subject (обратите внимание на пробелы, чтобы она не совпадала с другим полем, начинающимся с ‘subject’). Первые круглые скобки match извлекают строку поля, а вторые — доменное имя.

Цель [^,]+ — это соответствие всему до и не включая (следующую) запятую-разделитель в нелакомом (нежадном) режиме. Если бы вы использовали .+,, это захватило бы все до C=CA.

По завершении этого совпадения, arr содержит все регулярное выражение, строку поля и строку домена.

Мы пока не печатаем, потому что не знаем, что включать. (Вы также можете напечатать частичную строку, если хотите, и добавить END { printf "\n"}.)

Совпадение по / +subject-alternative-name смотрит на второе поле для ключевого слова DNS, захватывая все содержимое поля, и, если оно совпадает, печатает соответствующую расширенную команду, в противном случае печатает регулярную команду.

С удовольствием аукций!

Ответ или решение

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

Теория

При анализе данных важно понимать, что каждая строка в выходных данных начинается с определенного ключевого слова и содержит набор значений, которые необходимо извлечь и использовать для генерации команды. Нашей целью является извлечение имени файла на основании строки, содержащей CN= из строки с subject, и проверка наличия альтернативного имени через строку, содержащую subject-alternative-name.

Регулярные выражения и функция match()

Функция match() в awk предоставляет удобный способ искать и извлекать части строки, которые соответствуют заданному регулярному выражению. Функция принимает три параметра: строку, регулярное выражение и массив, в который будут помещены результаты. Регулярные выражения позволяют выделить конкретные части интереса, например, содержимое между CN= и последующей запятой или проверить наличие DNS:.

Обработка строк в awk

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

Пример

Чтобы иллюстрировать использование awk для решения поставленной задачи, приведём пример скрипта:

awk '
BEGIN { FS = "\n"; RS = ""; }
{
  subjectLine = ""
  dnsLine = ""

  # Обрабатываем каждую линию блока
  for (i=1; i<=NF; i++) {
    if ($i ~ /^ *subject /) {
      subjectLine = $i
    }
    if ($i ~ /^ *subject-alternative-name /) {
      dnsLine = $i
    }
  }

  # Извлекаем CN из subject
  if (match(subjectLine, /CN=([^,]+)/, cn)) {
    certName = cn[1]
  }

  # Извлекаем всю строку для subj
  if (match(subjectLine, /"(.*)"/, subj)) {
    subject = subj[1]
  }

  # Проверяем на наличие DNS
  hasDNS = 0
  if (match(dnsLine, /DNS:([^"]+)/, dns)) {
    altName = dns[1]
    hasDNS = 1
  }

  # Формируем строку команды
  cmd = "openssl req -new -nodes -sha256 -newkey rsa:2048 -out " certName "_2025.csr -keyout " certName "_2025.key -subj \"/" subject "\""
  if (hasDNS) {
    cmd = cmd " -addext \"subjectAltName = DNS:" altName "\""
  }
  print cmd
}' whatever.com

Применение

Этот скрипт обработает ваш файл whatever.com и сгенерирует нужные команды openssl. Скрипт проходит через каждый блок текста, определяя строки, содержащие subject и subject-alternative-name. Далее, с помощью функций match(), извлекаются необходимые значения для генерации команды. Обратите внимание, как из строки с CN= выделяется имя сертификата, а добавление опции -addext зависит от наличия DNS:, что учитывается при формировании конечной строки команды.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...