Заменить разделенный блок текста в файле содержимым другого файла - PullRequest
6 голосов
/ 23 апреля 2010

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

Предположим, у вас есть следующие упрощенные файлы:

server.xml

<?xml version='1.0' encoding='UTF-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="80" protocol="HTTP/1.1"/>
    <Engine name="Catalina" defaultHost="localhost">
      <!-- BEGIN realm -->
        <sometags/>
        <sometags/>
      <!-- END realm -->
      <Host name="localhost" appBase="webapps"/>
    </Engine>
  </Service>
</Server>

realm.xml

<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
       resourceName="UserDatabase"/>

Я хочу запустить скрипт и realm.xml заменить содержимое между строками <!-- BEGIN realm --> и <!-- END realm -->. Если realm.xml изменится, то при каждом повторном запуске сценария он будет снова заменять строки новым содержанием realm.xml. Он предназначен для запуска в /etc/init.d/tomcat при запуске службы в нескольких установках, в которых сфера будет другой.

Я не совсем уверен, как я могу сделать это просто с помощью awk или sed.

Ответы [ 7 ]

13 голосов
/ 23 апреля 2010

Попробуйте:

sed -i -ne '/<!-- BEGIN realm -->/ {p; r realm.xml' -e ':a; n; /<!-- END realm -->/ {p; b}; ba}; p' server.xml
3 голосов
/ 23 апреля 2010
TOTAL_LINES=`cat server.xml | wc -l`
BEGIN_LINE=`grep -n -e '<!-- BEGIN realm -->' server.xml | cut -d : -f 1`
END_LINE=`grep -n -e '<!-- END realm -->' server.xml | cut -d : -f 1`
TAIL_LINES=$(($TOTAL_LINES-$END_LINE))

head -n $BEGIN_LINE server.xml > server2.xml
cat realm.xml > server2.xml
tail -n $TAIL_LINES server.xml > server2.xml

(ОК, это не использует awk или sed ... Я предположил, что это не было исключительным требованием: -)

2 голосов
/ 23 апреля 2010

вы можете использовать awk

awk 'FNR==NR{ _[++d]=$0;next}
/BEGIN realm/{
  print
  for(i=1;i<=d;i++){ print _[i] }
  f=1;next
}
/END realm/{f=0}!f' realm.xml server.xml > temp && mv temp server.xml

realm.xml передается в awk как первый файл. FNR == NR означает получение записей первого переданного файла и сохранение в переменной _. awk обработает следующий файл, как только FNR! = NR. если awk находит /BEGIN realm/, выведите строку BEGIN realm, затем напечатайте то, что хранится в _. Если установить флаг (f) на 1, остальные строки после BEGIN realm не будут напечатаны, пока не будет обнаружено /END realm/.

1 голос
/ 23 апреля 2010

Вы также можете использовать команду ed (см. http://wiki.bash -hackers.org / howto / edit-ed ):

cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s server.xml
   H
   /BEGIN realm/i
   .
   /BEGIN realm/+1,/END realm/-1d
   .-1r realm.xml
   wq
EOF
1 голос
/ 23 апреля 2010

Как насчет этого небольшого фрагмента, который я создал:

sed -n \
  -e "1,/<\!-- BEGIN realm -->/ p" \
  -e"/<\!-- END realm -->/,$ p" \
  -e "/<\!-- BEGIN realm -->/ r realm.xml" \
  server.xml

Первая команда печатает строки до <!- BEGIN realm -->, вторая команда печатает строку, начинающуюся с <!-- END realm -->, а третьи команды добавляют текст в файл 'realm.xml'. Если бы я только мог упростить удаление линий между <!- BEGIN realm --> и <!-- END realm -->, не удаляя линии маркера, это было бы так же просто, как и получалось. И это можно сделать inplace с помощью sed !!!

0 голосов
/ 24 мая 2014

Мне не удалось получить решение Dennis, легко работающее на OS X (его BSD sed немного отличается). Я нашел это другое решение, которое мне удалось заставить работать как на Linux, так и на OS X (у меня смешанная среда). Оригинальная версия на superuser.com работает только на Linux, здесь я это исправил:

lead='^<!-- BEGIN realm -->$'
tail='^<!-- END realm -->'
sed  -e '/'"$lead"'/,/'"$tail"'/{ /'"$lead"'/{p; r realm.xml' -e' }; /'"$tail"'/p; d;} '  server.xml

Вот версия кода Денниса, которая работает также на OS X (используя несколько строк):

sed -ne '/'"$lead"'/ {
 p
 r realm.xml
 :a
 n 
 /'"$tail"'/ {
  p
  b
 } 
 ba
 }
p' server.xml

Оба эти кода выводят вывод на стандартный вывод. Используйте перенаправление или, чтобы заменить встроенный файл, добавьте параметр '-i' (в Linux) или '-i ""' (в BSD / OS X).

0 голосов
/ 16 февраля 2014

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

    python <<EOF
    import os, sys, re
    fname = 'server.xml'
    os.rename(fname, fname + '.orig')
    with open(fname + '.orig', 'r') as fin, open(fname, 'w') as fout:
        data = fin.read()

        data = re.sub(r'(<!-- BEGIN realm -->).*?(<!-- END realm -->)', 
          r'\1\n' +
          'insert whatever you want here\n' + 
          r'\2\n', data, flags=re.DOTALL)
        fout.write(data)
    EOF

Я думаю, что sed и awk имели своидень.Они были полезны когда-то, но очень немногие люди могут читать или писать без документальной помощи в эти дни.

(Источник: Интернет)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...