LINUX.ORG.RU

Парсинг и обработка файлов конфигов средствами BASH

 , ,


0

1

Имеются файлы конфигов OpenVpn, содержащие в себе такие строки:

  • <connection>
  • remote 192.168.255.1 1191
  • proto tcp
  • socks-proxy-retry
  • socks-proxy 127.0.0.1 1080
  • </connection>
  • <connection>
  • remote 192.168.254.1 1191
  • proto tcp
  • socks-proxy-retry
  • socks-proxy 127.0.0.1 1080
  • </connection>
  • <connection>
  • remote 192.168.245.1 1191
  • proto tcp
  • socks-proxy-retry
  • socks-proxy 127.0.0.1 1080
  • </connection>

Стоит задача автоматически добавить или удалить блок <connection> ... </connection> с заданным адресом. Добавление блока реализовал без проблем, но застопорился на удалении блока... С помощью sed -n «/connection/,/\/connection/p» /etc/openvpn/client/clientsconfig/$client_name я могу выделить все блоки - подскажите, как отфильтровать нужный блок (допустим, содержащий адрес 192.168.254.1) из выше приведенного списка ну и удалить его?


попробуй написать регэксп такого плана: /(<connection>.*?remote 192.168.245.1.*?</connection>)//

смысл в том чтобы заменить весь блок который начинается на <connection> и заканчивается на </connection>, и имеет remote <ip> в середине на пустое место.

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

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

anonymous
()
Ответ на: комментарий от anonymous

спасибо за подсказку, но похоже что-то не то: sed -n «/( .remote 192.168.245.1. )/» /etc/openvpn/client/clientsconfig/alham_win.ovpn в указанном файле ничего не делает (хотя адрес 192.168.245.1 там есть) и на экран ничего не выдает

Sanekk
() автор топика
Ответ на: комментарий от anonymous

извиняюсь - не заметил что не то вставил...

 sed -n "/(<connection>.*?remote 192.168.245.1.*?</connection>)/p" /etc/openvpn/client/clientsconfig/alham_win.ovpn 

вот это действие не приводит к желаемому результату (в теории я должен был получить из файла блок

 <connection>
remote 192.168.254.1 1191
proto tcp
socks-proxy-retry
socks-proxy 127.0.0.1 1080
</connection>

Sanekk
() автор топика
Ответ на: комментарий от frob

пытался играться и похоже он не любит перенос строки:

 sed -n "/(<connection>.*,</connection>)/p" /etc/openvpn/client/clientsconfig/alham_win.ovpn 
так отображает нормально (т.е. все блоки), но как только после «*» что-то пишу - сразу пустой вывод...

На просторах инета наткнулся на такой пример:

 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'
- «Печатать абзац если он содержит AAA (пустая строка разделяет абзацы)» ( https://unlix.ru/sed-примеры-однострочных-скриптов/ ) Т.е. он как-бы подходит для моего случая, но я не могу понять как в этом примере «пустую строку» заменить на «connection», т.к. между блоками может и не быть пустых строк

Sanekk
() автор топика
Ответ на: комментарий от wandrien

Спасибо, но возникло пару трудностей:

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

  2. ваш пример не работает - выдает листинг всего файла

Sanekk
() автор топика
Ответ на: комментарий от Sanekk

Вот сделал:

 sed -e '/./{H;$!d;}' -e 'x;/192.168.254.1/!d;' /etc/openvpn/client/clientsconfig/alham_win.ovpn 
- работает отлично - даже натолкнуло на мысль блоки для одного сервера (их может быть и два) объединить... Но остается задача «позаботиться о наличии строк-разделителей во всех конфигах», а этих файлов на боевом сервере почти 2 сотни... вот почему и мудрю над автоматикой

Sanekk
() автор топика
Ответ на: комментарий от Sanekk

ваш пример не работает - выдает листинг всего файла

Вызывающе неверная информация. Тестировал на тексте из поста:

vadim@aquila:/tmp/1$ cat 1.txt 
    <connection>
    remote 192.168.255.1 1191
    proto tcp
    socks-proxy-retry
    socks-proxy 127.0.0.1 1080
    </connection>
    <connection>
    remote 192.168.254.1 1191
    proto tcp
    socks-proxy-retry
    socks-proxy 127.0.0.1 1080
    </connection>
    <connection>
    remote 192.168.245.1 1191
    proto tcp
    socks-proxy-retry
    socks-proxy 127.0.0.1 1080
    </connection>
vadim@aquila:/tmp/1$ perl -0pe's/<connection>[^<>]*remote 192\.168\.245\.1 [^<>]*<\/connection>//ms' < 1.txt
    <connection>
    remote 192.168.255.1 1191
    proto tcp
    socks-proxy-retry
    socks-proxy 127.0.0.1 1080
    </connection>
    <connection>
    remote 192.168.254.1 1191
    proto tcp
    socks-proxy-retry
    socks-proxy 127.0.0.1 1080
    </connection>
    
vadim@aquila:/tmp/1$ 
wandrien ★★
()
Ответ на: комментарий от Sanekk

Печатать абзац если он содержит AAA

На соплях. Потом перестанет работать, и следующий админ будет искать тебя по всему городу с вилами.

wandrien ★★
()
Ответ на: комментарий от wandrien

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

anonymous
()
Ответ на: комментарий от Sanekk

А... хехе... не увидеть sed за всё это время...
который не умеет non-greedy и довольно интересно (в дурном смысле) работает с многострочным поиском.

Если внутри <connection>..</connection> нет пустых строк, можно сделать примерно так:
sed '/./{H;$!d} ; x ; s#<connection>.*192.168.254.1[^<]*</connection>##'

«Но нам всем будет лучше», если вместо sed будет использован инструмент умеющий мультилайн и non-greedy без извращений.

frob ★★★★★
()
Ответ на: комментарий от wandrien

На соплях. Потом перестанет работать, и следующий админ будет искать тебя по всему городу с вилами.

Это мой сервер...

Sanekk
() автор топика
Ответ на: комментарий от anonymous

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

Понимаю: !d не удалять найденное; d - удалить найденное

«Но нам всем будет лучше», если вместо sed будет использован инструмент умеющий мультилайн и non-greedy без извращений.

Для меня любое будет в новинку, а в sed меня яндекс загнал... вот и мучаюсь и других мучаю

Sanekk
() автор топика

Спасибо всем кто отозвался и пытался помочь.

Я решил все конфиги привести в порядок: поудалял лишние пустые строки и после «/connection» вставил пустую строку, т.е. у меня сейчас каждый блок <connection> ... </connection> разделяется пустыми строками и таким образом без проблем можно работать с этим парсером:

 sed -e '/./{H;$!d;}' -e 'x;/SHABLON/d;' file.conf 

Ну и соответственно поправил на правильность формирования дальнейшего конфига.

Sanekk
() автор топика

Мне кжается, что такую задачу удобнее решать на AWK:

awk -vDELETE="192.168.254.1 1191" '
/<connection>/ {
    in_conn = 1
    delete conn
    next
}
/<\/connection>/ {
    in_conn = 0
    if (conn["remote"][0] == " " DELETE) { next }
    print "<connection>"
    for (k in conn) {
        for (i in conn[k]) {
            print k conn[k][i]
        }
    }
    print
    next
}
( in_conn ) {
    k = $1
    $1 = ""
    conn[k][length(conn[k])] = $0
    next
}
{ print }
' /etc/openvpn/openvpn.conf
kmeaw ★★★
()

Или можно переписать это решение непосредственно на sh, но оно будет выглядеть (на мой взгляд) странно.

in_conn=0
remote=""

while read line; do
    case "$line" in
    "<connection>")
        conn=""
	remote=""
        in_conn=1
        ;;
    "</connection>")
        if [ "$remote" = "192.168.254.1" ]; then
            continue
        fi
        echo "<connection>$conn"
        echo "</connection>"
        in_conn=0
        ;;
    *)
        if [ $in_conn -eq 0 ]; then
            echo "$line"
	    continue
	fi
	set -- $line
	if [ "$1" = "remote" ]; then
            remote=$2
	fi
        conn="$conn
$line"
        ;;
    esac
done < /etc/openvpn/openvpn.conf
kmeaw ★★★
()
Последнее исправление: kmeaw (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.