替换文件中第一次出现的可能包含斜杠的模式

rat*_*noz 7 sed text-processing

感谢这个链接,我知道如何将包含斜杠的变量作为模式传递给 sed:

sed "s~$var~replace~g" $file. Juste 使用单字节字符代替 /。

多亏了这个其他链接,我还知道如何仅替换文件中第一次出现的模式(而不是在一行中):

sed "0,/$var/s/$var/replacement/" filename 或者 sed 0,/$var/{s/$var/replacement/} filename

但是如果我这样做:( sed '0,~$var~s~$var~replacement~' filename 或其他任何以 0 开头的,然后没有斜线),我就会得到一个错误:unknown command: '0'

我怎么能把两者结合起来?也许通过使用 awk 或 perl 或 ... ?

Sté*_*las 10

尽管:

sed "0,\~$var~s~$var~replacement~"
Run Code Online (Sandbox Code Playgroud)

可用于更改正则表达式分隔符,在sed(或任何其他解释器)代码中嵌入变量扩展在一般情况下是非常不明智的做法。

首先,这里的分隔符并不是唯一需要转义的字符。所有正则表达式运算符也需要这样做。

但更重要的是,特别是对于 GNU sed,这是一个命令注入漏洞。如果 的内容$var不在您的控制之下,那就像将任意数据传递给eval.

尝试例如:

$ var='^~s/.*/uname/e;#'
$ echo | sed "0,\~$var~s~$var~replacement~"
Linux
Run Code Online (Sandbox Code Playgroud)

uname命令已运行,幸好是一个无害命令……这次。

非 GNUsed实现不能运行任意命令,但可以覆盖任何文件(使用w命令),这实际上同样糟糕。

更正确的方法是$varfirst 中转义有问题的字符

NL='
'
case $var in
  (*"$NL"*)
    echo >&2 "Sorry, can't handle variables with newline characters"
    exit 1
esac
escaped_var=$(printf '%s\n' "$var" | sed 's:[][\/.^$*]:\\&:g')
# and then:
sed "0,/$escaped_var/s/$escaped_var/replacement/" < file
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用perl

var=$var perl -pe 's/\Q$ENV{var}\E/replacement/g && $n++ unless $n' < file
Run Code Online (Sandbox Code Playgroud)

请注意,我们没有扩展$var传递给的代码内部的内容perl(这将是另一个命令注入漏洞),而是让perl扩展其内容作为其正则表达式处理的一部分(\Q...\E这意味着不会对正则表达式运算符进行特殊处理)。

如果$var包含换行符,则可能仅在末尾只有一个时才匹配。或者,可以传递该-0777选项,以便将输入作为单个记录而不是逐行处理。