Вопрос или проблема
У меня есть вывод, похожий на файл (имя файла 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
является необходимым для решения подобных задач и позволяет эффективно обрабатывать и трансформировать текстовые данные.