Я храню свои данные SOA для нескольких доменов в одном файле, который получает $INCLUDE d по файлам зоны. Я написал небольшой сценарий sed, который должен получить серийный номер, увеличить его, а затем повторно сохранить файл SOA. Все работает правильно, пока файл SOA находится в правильном формате, со всей записью в одной строке, но он терпит неудачу, как только запись разбивается на несколько строк.

Например, это работает как входные данные:

@ IN SOA dnsserver. hostmaster.example.net. ( 2013112202 21600  900 691200 86400 )

Но это не так:

@ IN SOA dnsserver. hostmaster.example.net. (
                            2013112202      ; Serial number
                            21600           ; Refresh every day, 86400 is 1 day
                            900             ; Retry refresh every 15 min
                            691200          ; Expire every 8 days
                            86400 )         ; Minimum TTL 1 day

Мне нравятся комментарии, и я хотел бы рассказать о них. Но мне нужен мой сценарий, чтобы найти серийный номер, чтобы я мог увеличивать его и перезаписывать файл.

SED, который работает с одной строкой, выглядит следующим образом:

SOA=$(sed 's/.*@.*SOA[^0-9]*//;s/[^0-9].*//' $SOAfile)

Но для многострочного ... немного растерялся. Я знаю, что могу присоединиться к строкам с N, но как мне узнать, нужно ли ? Нужно ли мне писать отдельные сценарии sed на основе какого-либо другого анализа исходного файла?

Пожалуйста помоги! :-)

2
Graham 23 Апр 2014 в 00:16

4 ответа

Лучший ответ

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

Что насчет этого в awk?

Самый простой способ - разделить записи на основе символа @, например:

SOA=$(awk 'BEGIN{RS="@"} NR==2{print $6}' $SOAfile)

Но это не работает, если у вас есть комментарии, содержащие @ перед строкой без комментариев, или если у вас есть какие-либо комментарии между @ и серийным номером. Вы можете сделать трубку, чтобы избежать этих проблем ...

SOA=$(sed 's/;.*//;/^@/p;1,/^@/d' $SOAfile | awk 'BEGIN{RS="@"} NR==2{print $6}')

Удаление комментариев и может показаться излишним, но могут быть и другие строки, такие как #include, которые (что маловероятно) могут содержать ваш разделитель записей.

Или вы можете сделать что-то вроде этого на чистом awk:

SOA=$(awk -v field=6 '/^@/ { if($2=="IN"){field++} for(i=1;i<field;i++){if(i==NF){field=field-NF;getline;i=1}} print $field}' $SOAfile)

Или, разбитый для облегчения чтения:

awk -v field=6 '
  /^@/ {
    if ($2=="IN") {field++;}
    for (i=1;i<field;i++) {
      if(i==NF) {field=field-NF;getline;i=1;}
    }
    print $field; }' $SOAfile

Это достаточно гибко, чтобы справиться с любым разбиением строк, которое может у вас возникнуть, поскольку оно учитывает field по нескольким строкам. Он также регулирует номер поля в зависимости от того, содержит ли сегмент зоны необязательное ключевое слово «IN».

Решение на чистом sed вместо подсчета полей будет использовать первую строку цифр после открытой скобки после вашего /^@/, например:

SOA=$(sed -n '/^@/,/^[^;]*)/H;${;x;s/.*@[^(]*([^0-9]*//;s/[^0-9].*//;p;}' $SOAfile)

Похоже на линейный шум, правда? :-) Разбитое для удобства чтения выглядит так:

/^@/,/^[^;]*)/H              # "Hold" the meaningful part of the file...
${                           # Once we reach the end...
  x                          # Copy the hold space back to the main buffer
  s/.*@[^(]*([^0-9]*//       # Remove stuff ahead of the serial
  s/[^0-9].*//               # Remove stuff after the serial
  p                          # And print.
}

Идея заключается в том, что, начиная с первой строки, которая начинается с @, мы копируем файл в пространство хранения sed, а затем в конце файла делаем некоторые замены, чтобы вырезать весь текст до серийного номера. , а затем после серийного номера и распечатайте все, что осталось.

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

4
ghoti 22 Апр 2014 в 23:20

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

 SOA=$(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/.*@.*SOA[^0-9]*//;s/[^0-9].*//' \
   "$SOAfile")

Эта форма будет работать как с однострочными, так и с многострочными входными файлами.

Многострочные входные файлы сначала читаются целиком перед применением замен.

Примечание: неудобные отдельные параметры -e необходимы для того, чтобы FreeBSD оставалась довольной в отношении меток и команд ветвления, которым требуется буквальный \n для завершения - использование отдельных параметров -e является более удобочитаемой альтернативой. для объединения буквальных символов новой строки с $'\n'.


Альтернативное решение с использованием awk:

SOA=$(awk -v RS='@' '$1 == "IN" && $2 == "SOA" { print $6 }' "$SOAfile")

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

Единственное ограничение - комментарии не должны предшествовать серийному номеру.

Кроме того, если файл содержит несколько записей , в приведенном выше примере будут собраны ВСЕ серийные номера, разделенные новой строкой.

1
mklement0 22 Апр 2014 в 21:55

Почему sed? grep в этом случае самый простой:

grep -A1 -e '@.*SOA' 1 | grep -oe '[0-9]*'

Или: (может быть, лучше):

grep -A1 -e '@.*SOA' 1 | grep 'Serial number' | grep -oe '[0-9]*'
0
Radu Rădeanu 22 Апр 2014 в 20:58

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

sed -nr '/@ IN SOA/{/[0-9]/!N;s/[^0-9]+([0-9]+).*/\1/p}' file

Для строк, содержащих @ IN SOA, если строка не содержит чисел, добавьте следующую строку. Затем извлеките первую последовательность чисел из строки (строк).

0
potong 23 Апр 2014 в 10:36