我有一些配置文件基本上看起来像
(...content...)
# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY
(... more content ...)
# END DYNAMIC BLOCK
(... even more content ...)
现在,在 bash 中使用CONTENT=`wget -O - http://$SERVER/get_config.php`
,我有了动态块的替代品。
现在如何进行替换以及如何让脚本在文件末尾插入该块(如果不存在)?
答案1
如果你想使用 sed,你可以从命名管道读取。请注意,此代码不会尝试处理错误。如果动态块头多次出现,则脚本将被阻止。
CONTENT_URL="http://$SERVER/get_config.php"
tmp=$(mktemp -d)
(
cd "$tmp"
mkfifo dynamic_seen dynamic_content
: >dynamic_seen & seen_pid=$!
wget -O dynamic_content "$CONTENT_URL" & wget_pid=$!
sed -e '/^# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY$/ p' \
-e '/^# END DYNAMIC BLOCK$/ {'
-e p -e 'r dynamic_seen' -e 'r dynamic_content' -e '}' \
-e '/^# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY$/, /^# END DYNAMIC BLOCK$/ d'
if ! kill $dynamic_seen 2>/dev/null; then
# The pipe hasn't been read, so there was no dynamic block. Add one.
echo "# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY"
cat dynamic_pipe
echo "# END DYNAMIC BLOCK - DO NOT EDIT MANUALLY"
fi
)
rm -rf "$tmp"
但我会选择 awk。
export CONTENT_URL="http://$SERVER/get_config.php"
awk '
$0 == "# END DYNAMIC BLOCK - DO NOT EDIT MANUALLY" {skip=0; system("wget \"$CONTENT_URL\""); substituted=1}
!skip {print}
$0 == "# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY" {skip=1}
END {
if (!substituted) {
print "# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY";
system("wget \"$CONTENT_URL\"");
print "# END DYNAMIC BLOCK - DO NOT EDIT MANUALLY";
}
}
'
答案2
我会使用一个子 shell 和两个 sed 命令,如下所示:
beg_tag='# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY'
end_tag='# END DYNAMIC BLOCK'
(
sed "/^$beg_tag"'$/,$d' oldconf
echo "$beg_tag"
wget -O - http://$SERVER/get_config.php
echo "$end_tag"
sed "1,/^$end_tag/d" oldconf
) > newconf
小心不要将任何 sed 重要字符放入beg_tag
和中end_tag
中。
如果不存在标签,这将附加输出。第一个 sed 命令永远不会从输入中删除任何行,第二个 sed 命令将删除所有行。
测试
如果oldconf
包含:
(...content...)
# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY
(... more content ...)
# END DYNAMIC BLOCK
(... even more content ...)
并且wget命令替换为echo hello world
,输出为:
(...content...)
# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY
hello world
# END DYNAMIC BLOCK
(... even more content ...)
现在,如果删除该块,即使用以下输入:
(...content...)
(... even more content ...)
输出是:
(...content...)
(... even more content ...)
# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY
hello world
# END DYNAMIC BLOCK
答案3
这sed
实际上做起来相当简单。您只需要平衡各行之间的范围并锚定到 EOF。
INPUT |
sed -e 's/\\/&&/g;$!s/$/\\/' | #this sed escapes INPUT for scripting
sed -e '/^'"$START"'/,$!{$!b #this sed applies concatenated scripts
G;G;s/$/'"$END"'/;P;:n
};$!N; /\n'"$END"'/,$!{G;$!bn
}; /\n\n/c\' -f - -e 'P;$d;D
' ./named_infile >outfile
因此,那里发生了一些事情,但其中最重要的是:
/^$START/,$!{ -- function --}
N; /\n$END/,$!{ -- function -- }
这个想法是,当我们锚行范围为 Line1
或$
我们基本上刚刚完成贪婪的。通常,行范围仅适用于它们可能的行的最小子集 - 每个 LHS 匹配都重新开始,并以输入中下一个出现的第一个 RHS 匹配结束。如果 RHS 是 EOF,那么它们只能应用一次 - 因为只有其中一个。
当我做:
/^$START/,$!{ -- function -- }
我指定花括号之间的所有代码都针对 infile 中的每一行运行,但不是包括$START
。在这个函数上下文中,我为不是最后b
一行的每一行进行了扩展。!
$
通过这种方式,输入中第一行之前的所有行$START
都会自动打印并被忽略,但如果$
最后一行落在这个范围内 - 因为它可能$START
永远不会出现一次 - 那么它就准备好挂c
到你的字符串上。
因此,如果您的范围未出现在输入中,则 INPUT 会附加到文件的尾部。
当我下次这样做时:
N; /\n$END/,$!{ -- function -- }
我再次根据上下文应用函数。这次它应用于范围的主体 - 并且仅是它输入的第一次出现 - 因为 的补集是在第一个出现之前未排列的/\n$END/,$
所有行,并且仅直到但不包括下一个出现的行。b
$START
$END
在这种情况下,应用的函数是一个分支循环 - 只要输入落在该范围内,它就会继续b
回溯并拉入N
ext 行,直到找到第一个$END
匹配项,此时它将c
整个范围挂起到-f -
标准输入脚本文件 - 或您的转义输入。如果最后一行发生在第一个$START
匹配之前,则同样的规则将应用于最后一行。
就是这样。但请注意,这不需要任何特殊文件即可工作 - 因为它(安全地)包含一份副本输入在其脚本内,不需要r
随时将其写入以在需要时应用。