有没有办法阻止 sed 解释替换字符串?

有没有办法阻止 sed 解释替换字符串?

如果您想使用字符串替换关键字sedsed请努力解释您的替换字符串。如果替换字符串碰巧包含被sed视为特殊的字符(例如/字符),则它将失败,当然,除非您希望替换字符串包含告诉sed如何操作的字符。

前任:

VAR="hi/"

sed "s/KEYWORD/$VAR/g" somefile

有什么方法可以告诉sed不要尝试解释特殊字符的替换字符串吗?我想要的只是能够用变量的内容替换文件中的关键字,无论该内容是什么。

答案1

替换部分只有 4 个特殊字符:\&、 换行符和分隔符 (参考

$ VAR='abc/def&ghi\foo
next line'

$ repl=$(sed -e 's/[&\\/]/\\&/g; s/$/\\/' -e '$s/\\$//' <<<"$VAR")

$ echo "$repl"
abc\/def\&ghi\\foo\
next line

$ echo ZYX | sed "s/Y/$repl/g"
Zabc/def&ghi\foo
next lineX

答案2

您可以使用 Perl 代替 sed -p(假设循环输入)和-e(在命令行上给出程序)。使用 Perl,您可以访问环境变量没有在 shell 中插入这些。请注意,该变量需要是出口:

export VAR='hi/'
perl -p -e 's/KEYWORD/$ENV{VAR}/g' somefile

如果您不想在任何地方导出变量,则只需为该进程提供它:

PATTERN="$VAR" perl -p -e 's/KEYWORD/$ENV{PATTERN}/g' somefile

请注意,默认情况下 Perl 的正则表达式语法与 sed 的稍有不同。

答案3

仍然可以正确处理绝大多数变量值的最简单的解决方案是使用非打印字符作为sed替换命令的分隔符。

vi许多 shell 中,您可以通过键入 Ctrl-V(通常写为^V)来转义任何控制字符。因此,如果您使用一些控制字符(^A在这些情况下我经常用作分隔符),那么sed只有当您放入的变量中存在该非打印字符时,您的命令才会中断。

因此,您输入"s^V^AKEYWORD^V^A$VAR^V^Ag"的内容(在vi您的 shell 中)将如下所示:

sed "s^AKEYWORD^A$VAR^Ag" somefile

(您无法从此答案中复制并粘贴此答案。您必须按照描述实际输入它。)

只要$VAR不包含非打印字符,这就会起作用^A——这是极不可能的。


当然,如果你通过了用户输入的值$VAR,那么所有的赌注都会被取消,你最好彻底清理你的输入,而不是依赖于普通用户难以输入的控制字符。


不过,实际上除了分隔符字符串之外还有更多需要注意的地方。例如,&当出现在替换字符串中时,表示“匹配的整个文本”。例如,s/stu../my&/将“stuff”替换为“mystuff”,将“stung”替换为“mystung”等。所以如果您可能有任何您要放入的变量中作为替换字符串的字符,但您只想使用该变量的文字值,那么您需要先进行一些数据清理,然后才能将该变量用作 中的替换字符串sed。 (不过,数据清理也可以完成sed。)

答案4

如果它是基于行的并且只有一行要替换,我建议使用 ,在文件本身前面添加替换行printf,将第一行存储在sed的保留空间中,并根据需要将其放入。这样你就根本不用担心特殊字符。 (这里唯一的假设是$VAR包含一行没有任何换行符的文本,这就是您在评论中所说的。)除了换行符之外,VAR 还可以包含任何事无论如何,这都会起作用。

VAR=whatever
{ printf '%s\n' "$VAR";cat somefile; } | sed '1{h;d;};/KEYWORD/g'

printf '%s\n'会将 的内容打印$VAR为文字字符串,无论其内容如何,​​后跟换行符。 (echo在某些情况下会执行其他操作,例如,如果 的内容$VAR以连字符开头,它将被解释为传递给 的选项标志echo。)

大括号用于将 的输出添加到传递给 的printf内容之前。在这里,分隔大括号本身的空格很重要,右大括号之前的分号也很重要。somefilesed

1{h;d;};作为sed命令会将第一行文本存储在sed's中保留空间,然后d删除该行(而不是打印它)。

/KEYWORD/将以下操作应用于包含 的所有行KEYWORD。操作是get,它获取保留空间的内容并将其替换为模式空间——换句话说,整个当前行。 (这不仅仅是为了替换部分顺便说一句,保留空间并没有被清空,只是复制的进入模式空间,替换其中的任何内容。

如果你想你的正则表达式所以它不会匹配仅仅包含KEYWORD 但只有一行,除了 KEYWORD 之外没有其他内容,请在正则表达式中添加行首锚点 ( ^) 和行尾锚点 ( ):$

VAR=whatever
{ printf '%s\n' "$VAR";cat somefile; } | sed '1{h;d;};/^KEYWORD$/g'

相关内容