Извлечение определений объектов Nagios из монолитного файла конфигурации в отдельные файлы.

Я переношу некоторые старые данные конфигурации Nagios из Nagios Core в Nagios XI. Часть этой работы означает, что мне нужно извлечь некоторые определения объектов и поместить их в отдельные файлы, названные по имени хоста (пример ниже). Я вижу несколько способов сделать это, возможно, написав скрипт (Perl/Python/PHP - кажется, что все сценарии Nagios XI выполняются на PHP). Однако мне было интересно, есть ли более простой способ сделать это, возможно, в командной строке с использованием awk или аналогичного? Меня поражает, что awk может достаточно легко извлекать строки текста между двумя шаблонами-разделителями (например, /define host \{/ и /\}/), но мне нужно, чтобы вывод был разделен на отдельные файлы, названные по содержимому поля host_name.

Каков наилучший подход к этому? Мне лучше написать сценарий или есть удобная команда awk (или аналогичная), которую можно запустить из оболочки bash на машинах Nagios XI?

Пример монолитного файла:

define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
define service {
    use     servtemplate1
    host_name   testhost1
    service_groups  +All
    service_description  A Test Service
}
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
}

Желаемый результат:

# cat testhost1.cfg
define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
# cat testhost2.cfg
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
 }

Теперь, например, я могу запустить такую ​​команду, которая довольно широко используется для извлечения строки:

# gawk ' /define host / {flag=1;next} /}/{flag=0} flag { print }' example.cfg

Это обрезает define host и }, но это относительно легко исправить, однако данные выводятся как один поток в оболочке.

Есть ли какой-нибудь хитрый трюк, который я могу реализовать, чтобы сделать все это, включая разделение на отдельные файлы конфигурации из одного вкладыша в оболочке, или мне нужно написать сценарий?


person James Freeman    schedule 13.09.2017    source источник
comment
Любопытно, почему мой вопрос был отклонен, поскольку я здесь новичок - решения, похоже, помогли другим, помимо меня, так что не делает ли это само по себе вопрос ценным? Если я пойму причину, я позабочусь о том, чтобы будущие вопросы были лучше. Спасибо!   -  person James Freeman    schedule 15.09.2017


Ответы (2)


Используя авк:

Однострочник

awk '/^define host/{f=1;str=$0;next}/host_name/{h=$NF".cfg"}f{str=str ORS $0}f && /^\}/{print "#"h>h; print str>h; f=""; close(h)}' file

Пояснение

awk '
      /^define host/{                # look for line start with define host
                      f=1            # set variable f to 1
                      str=$0         # set variable str = current line/row/record 
                      next           # go to next line
      } 
      /host_name/{                   # look for line with hostname
                     h=$NF".cfg"     # set variable h with last field value plus ".cfg"
      }
      f{                             # if f was true or 1 then
                     str=str ORS $0  # concatenate variable str with current record 
      }
      f && /^\}/{                    # if f is true and line starts with } then
                     print "#"h > h  # write # hostname to file
                     print str > h   # write the content of variable str to file
                     f=""            # nullify variable
                     close(h)        # close file 
      }
    ' file

Результаты тестирования

Вход:

$ cat file 
define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
define service {
    use     servtemplate1
    host_name   testhost1
    service_groups  +All
    service_description  A Test Service
}
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
}

Исполнение:

$ awk '/^define host/{f=1;str=$0;next}/host_name/{h=$NF".cfg"}f{str=str ORS $0}f && /^\}/{print "#"h>h; print str>h; f=""; close(h)}' file

Сгенерированные файлы:

$ cat *.cfg
#testhost1.cfg
define host {
    host_name   testhost1
    use             hosttemplate1
    address                 10.0.0.1
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost1
    icon_image      redhat_icon.png
}
#testhost2.cfg
define host {
    host_name   testhost2
    use             hosttemplate2
    address                 10.0.0.2
    host_groups                     +linux,all
    contact_groups          +servicedesk
    alias           testhost2
    icon_image      redhat_icon.png
}

В PHP

$ cat test.php
<?php
preg_match_all('~define host\s+?{[^}]*}~', file_get_contents('file'), $match);
foreach($match[0] as $config)
{
    if(preg_match('~host_name\s+([^\s]*)~', $config, $host))
    {
        $file = $host[1].".cfg";
        file_put_contents($file, '#'.$file.PHP_EOL.$config.PHP_EOL);
    }
}
?>

$ php test.php 
$ ls *.cfg
testhost1.cfg  testhost2.cfg
person Akshay Hegde    schedule 13.09.2017
comment
привет @akshay-hegde! Полезный скрипт, я его запаковал и залил на SparrowHub, чтобы каждый мог его использовать повторно (плюс добавил несколько параметров и документацию) - sparrowhub.org/info/nagios-split-host - person Alexey Melezhik; 13.09.2017
comment
Это потрясающий ответ и так много деталей и объяснений - я знаю некоторые основы работы с awk, но ничего подобного этому уровню детализации - большое спасибо за вашу помощь! - person James Freeman; 15.09.2017

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

awk '/^}$/ && flag{print > file;flag="";close(file);next} /define host/{flag=1;val=$0;next} flag && /host_name/{file=$2".cfg";print val ORS $0 > file;next} flag{print > file}'   Input_file

РЕДАКТИРОВАТЬ: добавление решения, не состоящего из одного вкладыша.

awk '
/^}$/ && flag{             ##Looking for a line which starts from } and ends with same only + checking if variable flag is NOT null.
   print > file;           ##printing the current line to variable file(which will be having values like testhost1, testhost2.cfg etc etc
   flag="";                ##Nullifying the variable flag here.
   close(file)             ##Closing the file because in case of Input_file is huge so in backend these files(testconfig1 etc etc) could be opened and which may cause issues in case they all are opened, so it is good practice to close them.
   next                    ##Using next keyword of awk will skip all the further statements for the current line.
}
/define host/{             ##Searching for a line which has string define_host in it.
   flag=1;                 ##Making variable flag value to TRUE now, to keep track that we have seen string define_host in Input_file.
   val=$0;                 ##Storing this current line to a variable named val now.
   next                    ##Using next keyword will skip all further statements for the current line.
}
flag && /host_name/{       ##Checking if flag is TRUE and current line has string host_name in it then do following.
   file=$2".cfg";          ##Creating a variable named file who has value of 2nd field and string .cfg, this is actually the file names where we will save the contents
   print val ORS $0 > file;##printing variable named val then output record separator and current line and redirecting them to variable file which will make sure it should be saved in file named testhost etc etc .cfg.
   next                    ##As mentioned before too, next will skip all further statements.
}
flag{                      ##Checking if variable flag is TRUE or NOT NULL here.
   print > file            ##Printing the current line into the file then.
}
'  Input_file              ##Mentioning the Input_file here.
person RavinderSingh13    schedule 13.09.2017
comment
Большое спасибо за вашу помощь - добавление комментариев действительно помогло мне расширить свои знания об awk и в то же время решить мою проблему. - person James Freeman; 15.09.2017