awk/sed: 在特定块号的最后一行之前插入文件内容
awk/sed: Insert file content before last line of specific block number
给定两个文件,第一个是 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
我不知道如何 select 只有我需要插入文件的一个 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 }' | \
sed -e 's|(\(.*\))||' | \
cut -d: -f2
输出是我要扩展 FILE_2
的 BLOCK 的编号
请只使用非 GNU 版本,因为我使用的是 FreeBSD,谢谢。
awk
救援!
需要多字符记录分隔符,gawk
支持
$ awk 'NR==FNR{insert=[=10=]; next}
{print [=10=] (FNR==2?insert:"") RT}' RS='^$' insert.file RS="</VirtualHost>" file
完整读取第一个文件并赋值给变量insert,同时在第二条记录的末尾迭代第二个文件,在记录内容后打印变量。
纯文本的另一个版本awk
$ awk 'NR==FNR{insert=insert?insert ORS [=11=]:[=11=]; next}
/<\/VirtualHost>/ && ++c==2{print insert} 1' insert.file file
在 GNU sed
(和 BusyBox sed
)中 file/label/text 在 a
、b
、c
、[=17 之后=]、r
、t
、w
和 :
命令可以用分号分隔,而在其他版本的 sed
中,file/label/text 只能用换行符分隔。
该行为意味着不是定义标签 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
请注意,这种拆分在必须转义换行符的情况下可能不起作用;例如,当使用 a
、c
和 i
命令时。
鉴于:
$ 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 [=11=] "\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
给定两个文件,第一个是 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
我不知道如何 select 只有我需要插入文件的一个 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 }' | \
sed -e 's|(\(.*\))||' | \
cut -d: -f2
输出是我要扩展 FILE_2
的 BLOCK 的编号请只使用非 GNU 版本,因为我使用的是 FreeBSD,谢谢。
awk
救援!
需要多字符记录分隔符,gawk
$ awk 'NR==FNR{insert=[=10=]; next}
{print [=10=] (FNR==2?insert:"") RT}' RS='^$' insert.file RS="</VirtualHost>" file
完整读取第一个文件并赋值给变量insert,同时在第二条记录的末尾迭代第二个文件,在记录内容后打印变量。
纯文本的另一个版本awk
$ awk 'NR==FNR{insert=insert?insert ORS [=11=]:[=11=]; next}
/<\/VirtualHost>/ && ++c==2{print insert} 1' insert.file file
在 GNU sed
(和 BusyBox sed
)中 file/label/text 在 a
、b
、c
、[=17 之后=]、r
、t
、w
和 :
命令可以用分号分隔,而在其他版本的 sed
中,file/label/text 只能用换行符分隔。
该行为意味着不是定义标签 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
请注意,这种拆分在必须转义换行符的情况下可能不起作用;例如,当使用 a
、c
和 i
命令时。
鉴于:
$ 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 [=11=] "\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