英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论