awk / sed:在特定块号的最后一行之前插入文件内容

时间:2016-10-16 01:49:43

标签: shell unix awk sed freebsd

给定两个文件,第一个是Apache配置文件:

$ cat vhosts-ssl.conf
<VirtualHost *:443>
  vhost 1
  foobar 1
  foobar 2
  barfoo 1
  barfoo 2
</VirtualHost>

<VirtualHost *:443>
  vhost 2
foobar 2
    barfoo 1
 foobar 1
   barfoo 2
</VirtualHost>
<VirtualHost *:443>
vhost 3
  foobar 1

   barfoo 1
 foobar 2

  barfoo 2
</VirtualHost>

<VirtualHost *:443>

    vhost 4
 foobar 1
   foobar 2

  barfoo 1
barfoo 2

</VirtualHost>

第二个文件包含应添加到一个(变量)特定VirtualHost块的末尾的行:

$ cat inserted.txt
inserted line 1
 inserted line 2

结果应如下所示:

$ cat vhosts-ssl.conf
<VirtualHost *:443>
  vhost 1
  foobar 1
  foobar 2
  barfoo 1
  barfoo 2
</VirtualHost>

<VirtualHost *:443>
  vhost 2
foobar 2
    barfoo 1
 foobar 1
   barfoo 2
inserted line 1
 inserted line 2
</VirtualHost>
<VirtualHost *:443>
vhost 3
  foobar 1

   barfoo 1
 foobar 2

  barfoo 2
</VirtualHost>

<VirtualHost *:443>

    vhost 4
 foobar 1
   foobar 2

  barfoo 1
barfoo 2

</VirtualHost>

我尝试了以下sed的一些变体,但是没有做到这一点:

$ sed -e '/^<VirtualHost/{:a;n;/^<\/VirtualHost/\!ba;r inserted.txt' -e '}' vhosts-ssl.conf

我无法弄清楚如何只选择我需要插入文件的一个VirtualHost块,因为我使用FreeBSD sed(或awk)我也使用之前的sed命令得到此错误:

$ sed -e '/^<VirtualHost/{:a;n;/^<\/VirtualHost/\!ba;r inserted.txt' -e '}' vhosts-ssl.conf
sed: 2: "}
": unused label 'a;n;/^<\/VirtualHost/!ba;r inserted.txt'

使用GNU sed我得到这个输出:

$ gsed -e '/^<VirtualHost/{:a;n;/^<\/VirtualHost/\!ba;r inserted.txt' -e '}' vhosts-ssl.conf
<VirtualHost *:443>
  vhost 1
  foobar 1
  foobar 2
  barfoo 1
  barfoo 2
</VirtualHost>
inserted line 1
 inserted line 2


<VirtualHost *:443>
  vhost 2
foobar 2
    barfoo 1
 foobar 1
   barfoo 2
</VirtualHost>
inserted line 1
 inserted line 2

<VirtualHost *:443>
vhost 3
  foobar 1

   barfoo 1
 foobar 2

  barfoo 2
</VirtualHost>
inserted line 1
 inserted line 2


<VirtualHost *:443>

    vhost 4
 foobar 1
   foobar 2

  barfoo 1
barfoo 2

</VirtualHost>
inserted line 1
 inserted line 2

由于我想了解我的错误并从他们那里学习,我希望得到一些解释的答案,甚至可能还有一些链接到rtfm,谢谢。

已添加2016-10-16

伪代码:

if BLOCK begins with /^<VirtualHost/
    and ends with /^<\/VirtualHost/
        and is the ${n-th} BLOCK
            in FILE_1
then insert content of FILE_2
    before last line of ${n-th} BLOCK
        without touching rest of FILE_1
endif
save modified FILE_1

$ {n-th}收集于:

$ httpd -t -D DUMP_VHOSTS | \
    grep -i "${SUBDOMAIN}.${DOMAIN}" | \
    awk '/^[^\ ]*:443[\ ]*/ {print $3}' | \
    sed -e 's|(\(.*\))|\1|' | \
    cut -d: -f2

输出是我想通过FILE_2

扩展的BLOCK的编号

请在FreeBSD上只使用非GNU版本,谢谢。

3 个答案:

答案 0 :(得分:2)

awk救援!

需要多个字符记录分隔符,由gawk

支持
$ awk 'NR==FNR{insert=$0; next} 
  {print $0 (FNR==2?insert:"") RT}' RS='^$' insert.file RS="</VirtualHost>" file 

读完第一个文件并分配给变量insert,同时在第二个记录末尾迭代第二个文件,在记录内容后打印变量。

普通awk

的另一个版本
$ awk 'NR==FNR{insert=insert?insert ORS $0:$0; next} 
       /<\/VirtualHost>/ && ++c==2{print insert} 1' insert.file file

答案 1 :(得分:0)

在GNU sed(以及BusyBox sedabci之后的文件/标签/文字,rtw:命令可以用分号分隔,而在其他版本的sed中,文件/标签/文字可以分隔只能用换行符分隔。

这种行为意味着第一个字符串不是定义标签a,而是定义标签
a;n;/^<\/VirtualHost/\!ba;r inserted.txt,就像单独使用-e作为右括号一样,脚本必须在标签和分支之后分开。
(另外,!不得转义)

sed -e '/^<VirtualHost/{:a' -e 'n;/^<\/VirtualHost/!ba' \
    -e 'r inserted.txt' -e '}' vhosts-ssl.conf

或者,脚本可以跨越多行:

sed '/^<VirtualHost/ {
        :a
        n
        /^<\/VirtualHost/!ba
        r inserted.txt
}' vhosts-ssl.conf

请注意,在必须转义换行符的情况下,此拆分可能不起作用;例如,使用aci命令时。

答案 2 :(得分:0)

假设:

$ cat f1.txt
line 1
line 2
line 3
INSERT HERE
line 4
line 5
$ cat f2.txt
INSERTED LINE 1
INSERTED LINE 2

你可以这样做:

$ awk 'BEGIN{fc=""} FNR==NR{fc=fc $0 "\n";next} /^INSERT HERE/{printf "%s", fc; next} 1' f2.txt f1.txt
line 1
line 2
line 3
INSERTED LINE 1
INSERTED LINE 2
line 4
line 5