在以下示例文件中
more ambari-agent.ini
[server]
hostname=AABB
我们想要匹配文件中的单词并仅替换(等于字符)hostname
之后的值=
所以我们就这么做了
VAR=server_100
sed -ire "s/(hostname=)[^=]*$/\$VAR/" /tmp/ambari-agent.ini
但我们仍然有
more ambari-agent.ini
[server]
hostname=AABB
虽然预期结果应该是
more ambari-agent.ini
[server]
hostname=server_100
我们哪里错了?
答案1
VAR=anything
escaped_VAR=$(
printf '%s\n' "$VAR" |
LC_ALL=C sed 's|[/&\\]|\\&|g;$!s/$/\\/'
)
LC_ALL=C sed -E -i -e "s/^(hostname=).*/\1$escaped_VAR/" -- "$the_file"
对于 FreeBSD/macOS sed
,替换-i
为-i ''
.
对于 GNU (以及最终复制 GNU 而不是 FreeBSD 的 NetBSD/OpenBSD)和 FreeBSD sed
,-i
采用一个参数,即备份扩展的名称。对于 GNU sed
,该参数是可选的,并且必须坚持该-i
选项,而对于 FreeBSD sed
,它是必需的(但传递一个空参数也会禁用保留备份副本)。
-ire
对于 GNU 和 FreeBSD 都被解释为-i
with as 参数。在 FreeBSD 中被解释为带参数,而在 GNU 中则被解释为不带参数后跟选项。re
-i -e
-i
-e
-i
-e
sed
其他注意事项:
- GNU 用于
-r
ERE,BSD 用于-E
.-E
好多了,现在大多数实现都已经解决了,包括 GNUsed
。这将由 POSIX 指定。无论如何,这里 ERE 都是不必要的。 ERE 相对于 BRE 的唯一特征是|
(交替),其余的只是语法差异。sed -i "s/^\(hostname=\).*/\1$escaped_VAR/"
也会同样有效。 .*
C
如果输入包含在除 之外的区域设置中不形成有效字符的字节序列(因此为LC_ALL=C
),则可能无法匹配,直到行尾。- 如果内容未经净化并且 、 和换行符被转义,则在传递到的代码中扩展 asis 将构成命令注入
$VAR
漏洞。像您一样使用,只会替换为文字,而不是 shell 变量的内容。sed
&
\
/
\$VAR
$VAR
$VAR
- 如果值(或者其右侧的任何内容也可能是注释)不包含,则执行操作
s/(hostname=)[^=]*$/$escaped_VAR/
只会替换该行。我没想到这是故意的。hostname
hostname=
=
- 如果没有
^
,它将匹配hostname=
文件中的任何位置。^
确保仅完成hostname=
在行开头找到的实例。如果您想允许前导空格,您可以随时使用"/^([[:space:]]*hostname...
1 POSIX ERE 也缺少 BRE 的反向引用功能,尽管大多数 ERE 实现现在都支持它们作为扩展
答案2
前面的答案也可以作为一个函数,我只是缺乏声誉来回答那里的评论。
VAR=anything
function escape() { printf '%s\n' "$1" | LC_ALL=C sed 's|[+/&\\]|\\&|g;$!s/$/\\/' }
LC_ALL=C sed -E -e "s/^(hostname=).*/\1$(escape $VAR)/" -i -- "$file"
请注意,我已将+
字符修改为转义规则。
这也适用于动态提供VAR
或以其他方式提供的替换值。
LC_ALL=C sed -E -e "s/^(> API_SECRET=).*/\1$(escape $(pwgen -n 64 1))/" -i -- diff.patch
VAR="admin"; LC_ALL=C sed -E -e "s/^(+SP_ADMIN_USER=).*/\1$(escape "${VAR}")/" -i diff.patch
该命令与原始示例的不同之处在于省略了可能冗余的-e
and --
。
可以用另一个函数进一步参数化这个逻辑,VAR
完全省略。
function replace() { LC_ALL=C sed -E -e "s/^($(escape "${1}")).*/\1$(escape "${2}")/" -i "${3}" }
replace "+ADMIN_USER=" "admin" diff.patch
然后可以以编程方式使用它来替换所选行开头之后的值。