sed: -e expression #1, char 11: unterminated `s' command

huangapple go评论88阅读模式
英文:

sed: -e expression #1, char 11: unterminated `s' command

问题

我正在尝试将文件中的一些文本替换为另一个文本。我尝试了以下代码:

#!/bin/bash

file="path/to/file.txt"
file_contents=$(cat "$file")
escaped_replacement="Some other text"
sed -i "s/$file_contents/$escaped_replacement/g" "$file"

它给我一个错误 sed: -e expression #1, char 11: unterminated s' command`。

文件的内容如下:

This
is
an
example

但是当我将文本更改为 This is an example 时,它可以正常工作。

看起来当文本中有换行符时,它会出现此错误。我该如何解决这个问题?

我需要从一些文件中用另一个文本替换一些文本。

英文:

I am trying to replace some texts of a file with another text. I tried following

#!/bin/bash

file="path/to/file.txt"
file_contents=$(cat "$file")
escaped_replacement="Some other text"
sed -i "s/$file_contents/$escaped_replacement/g" "$file"

It gives me error sed: -e expression #1, char 11: unterminated s' command`.

The file has the content

This
is
an
example

But when I change the text to This is an example it works perfectly okay.

It looks like when there is a newline in the text that I want to replace, it gives this error. How can I solve this?

I need to replace some texts with another text from some files.

答案1

得分: 2

sed 逐行处理输入,所以无法匹配多行字符串。

你可以使用 Perl,它可以将整个文件读入内存(这就是 -0777 的作用)。

perl -0777 -pi -e "s/$file_contents/$escaped_replacement/g" "$file"

如果文件中包含斜杠,仍然可能会出现问题。你可以通过在 Perl 看到之前使用环境变量而不是扩展 shell 变量的方式来防止这种情况:

fc=$file_contents perl -0777 -pi -e "s/$ENV{fc}/$escaped_replacement/g" "$file"

(可能还需要对另一个变量执行相同操作,然后可以在表达式中使用单引号,参见下文)。

如果文件包含特殊的正则表达式构造(例如 {1,3}),并且你希望按字面意义替换它们,你可能需要在模式前面加上 \Q。这将调用 quotemeta 来为你转义所有特殊字符。

fc=$file_contents er=$escaped_replacement perl -0777 -pi -e \
    's/\Q$ENV{fc}/$ENV{er}/g' "$file"

如果你坚持要使用 sed,以下是一些尝试的步骤:

似乎你可以在新行前面加上反斜杠,以防止 sed 抱怨语法:

file_contents=${file_contents//$'\n'/$'\\\n'}

但是,即使语法是正确的,你也无法以这种方式匹配多行字符串,除非你使用 GNU sed。它可以使用 -z 类似于上面的 Perl 一样加载整个文件,所以如果你使用上面提到的替换,你可以运行:

sed -i -z "s=$file_contents=$escaped_replacement=" "$file"

如果文件中包含斜杠、反斜杠等,你可能还需要进行更多的替换。例如:

file_contents=${file_contents//\'/\'\\'}  # 转义反斜杠。
file_contents=${file_contents//\//\'\/\'}  # 转义斜杠。
英文:

sed processes the input line by line, so you can't match a multiline string.

You can use Perl which can read the whole file into memory (that's what the -0777 does).

perl -0777 -pi -e "s/$file_contents/$escaped_replacement/g" "$file"

This can still break if the file contains a slash. You can prevent this by using an ENV variable instead of expanding the shell variable in the command before Perl sees it:

fc=$file_contents perl -0777 -pi -e "s/$ENV{fc}/$escaped_replacement/g" "$file"

(and probably do the same with the other variable, then you can use single quotes for the expression, see below).

If the file contains special regex constructs (e.g. {1,3}) and you want to replace them literally, you might need to precede the pattern with a \Q. This will call quotemeta to escape all the special characters for you.

fc=$file_contents er=$escaped_replacement perl -0777 -pi -e \
    's/\Q$ENV{fc}/$ENV{er}/g' "$file"

If you insist on using sed, here are some steps to try:

It seems you can prefix newlines with backslashes to prevent sed from complainig about syntax:

file_contents=${file_contents//$'\n'/$'\\\n'}

But you can't match a multiline string this way, even if the syntax was correct - unless you use GNU sed. It can load the whole file for you with -z, similarly to Perl above, so if you use the substitution mentioned above, you can run

sed -i -z "s=$file_contents=$escaped_replacement=" "$file"

You might also need to do more substitutions if the file contains slashes, backslashes, etc. For example,

file_contents=${file_contents//'\'/'\\'}  # Escape backslashes.
file_contents=${file_contents//'/'/'\/'}  # Escape slashes.

huangapple
  • 本文由 发表于 2023年6月15日 16:36:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76480627.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定