bash 在 sed 的正则表达式中转义美元符号

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35506303/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 14:16:07  来源:igfitidea点击:

Escape dollar sign in regexp for sed

regexbashshellsed

提问by Captain Ahab

I will introduce what my question is about before actually asking - feel free to skip this section!

在实际提问之前,我将介绍我的问题是什么 - 随意跳过本节!

Some background info about my setup

关于我的设置的一些背景信息

To update files manually in a software system, I am creating a bash script to remove all files that are not present in the new version, using diff:

为了在软件系统中手动更新文件,我创建了一个 bash 脚本以使用 diff 删除新版本中不存在的所有文件:

for i in $(diff -r old new 2>/dev/null | grep "Only in old" | cut -d "/" -f 3- | sed "s/: /\//g"); do echo "rm -f $i" >> REMOVEOLDFILES.sh; done

This works fine. However, apparently my files often have a dollar sign ($) in the filename, this is due to some permutations of the GWT framework. Here is one example line from the above created bash script:

这工作正常。然而,显然我的文件在文件名中经常有一个美元符号 ($),这是由于 GWT 框架的一些排列。这是上面创建的 bash 脚本中的一行示例:

rm -f var/lib/tomcat7/webapps/ROOT/WEB-INF/classes/ExampleFile.class

Executing this script would not remove the wanted files, because bash reads these as argument variables. Hence I have to escape the dollar signs with "\$".

执行此脚本不会删除所需的文件,因为 bash 将这些文件作为参数变量读取。因此我必须用“\$”来逃避美元符号。

My actual question

我的实际问题

I now want to add a sed-Command in the aforementioned pipeline, replacing this dollar sign. As a matter of fact, sed also reads the dollar sign as special character for regular expressions, so obviously I have to escape it as well. But somehow this doesn't work and I could not find an explanation after googling a lot.

我现在想在上述管道中添加一个 sed-Command,替换这个美元符号。事实上,sed 也将美元符号读作正则表达式的特殊字符,所以显然我也必须转义它。但不知何故这不起作用,我在谷歌搜索后找不到解释。

Here are some variations I have tried:

以下是我尝试过的一些变体:

echo "Bla$bla" | sed "s/$/2/g"        # Output: Bla2
echo "Bla$bla" | sed 's/$$/2/g'        # Output: Bla
echo "Bla$bla" | sed 's/\$/2/g'       # Output: Bla
echo "Bla$bla" | sed 's/@"$"/2/g'     # Output: Bla
echo "Bla$bla" | sed 's/\$/2/g'      # Output: Bla

The desired output in this example should be "Bla2bla". What am I missing? I am using GNU sed 4.2.2

此示例中所需的输出应为“Bla2bla”。我错过了什么?我正在使用 GNU sed 4.2.2

EDIT

编辑

I just realized, that the above example is wrong to begin with - the echo command already interprets the $ as a variable and the following sed doesn't get it anyway... Here a proper example:

我刚刚意识到,上面的例子一开始是错误的——echo 命令已经将 $ 解释为一个变量,而下面的 sed 无论如何都没有得到它......这是一个正确的例子:

  1. Create a textfile testwith the content bla$bla
  2. cat testgives bla$bla
  3. cat test | sed "s/$/2/g"gives bla$bla2
  4. cat test | sed "s/\$/2/g"gives bla$bla2
  5. cat test | sed "s/\\$/2/g"gives bla2bla
  1. 创建一个test包含内容的文本文件bla$bla
  2. cat testbla$bla
  3. cat test | sed "s/$/2/g"bla$bla2
  4. cat test | sed "s/\$/2/g"bla$bla2
  5. cat test | sed "s/\\$/2/g"bla2bla

Hence, the last version is the answer. Remember: when testing, first make sure your test is correct, before you question the test object........

因此,最后一个版本就是答案。切记:在测试时,首先要确保你的测试是正确的,然后再质疑测试对象......

采纳答案by chepner

There are other problems with your script, but file names containing $are not a problem if you properly quote the argument to rmin the resulting script.

您的脚本还有其他问题,但$如果您rm在生成的脚本中正确引用了参数,包含的文件名就不是问题。

echo "rm -f '$i'" >> REMOVEOLDFILES.sh

or using printf, which makes quoting a little nicer and is more portable:

或者使用printf,这使得引用更好一点并且更便携:

printf "rm -f '%s'" "$i" >> REMOVEOLDFILES.sh

(Note that I'm addressing the real problem, not necessarily the question you asked.)

(请注意,我正在解决真正的问题,不一定是您提出的问题。)

回答by Captain Ahab

The correct way to escape a dollar sign in regular expressions for sed is double-backslash. Then, for creating the escaped version in the output, we need some additional slashes:

在 sed 的正则表达式中转义美元符号的正确方法是双反斜杠。然后,为了在输出中创建转义版本,我们需要一些额外的斜线:

cat filenames.txt | sed "s/\$/\\$/g" > escaped-filenames.txt

Yep, that's four backslashes in a row. This creates the required changes: a filename like bla$1$2.classwould then change to bla\$1\$2.class. This I can then insert into the full pipeline:

是的,这是连续四个反斜杠。这将创建所需的更改:文件名将bla$1$2.class更改为bla\$1\$2.class. 然后我可以将其插入到完整的管道中:

for i in $(diff -r old new 2>/dev/null | grep "Only in old" | cut -d "/" -f 3- | sed "s/: /\//g" | sed "s/\$/\\$/g"; do echo "rm -f $i" >> REMOVEOLDFILES.sh; done

Alternative to solve the background problem

替代解决背景问题

chepnerposted an alternative to solve the backround problem by simply adding single-quotes around the filenames for the output. This way, the $-signs are not read as variables by bash when executing the script and the files are also properly removed:

chepner发布了一个替代方案来解决后台问题,只需在输出的文件名周围添加单引号即可。这样,执行脚本时 $-signs 不会被 bash 读取为变量,并且文件也被正确删除:

for i in $(diff -r old new 2>/dev/null | grep "Only in old" | cut -d "/" -f 3- | sed "s/: /\//g"); do echo "rm -f '$i'" >> REMOVEOLDFILES.sh; done

(note the changed echo "rm -f '$i'"in that line)

(注意该echo "rm -f '$i'"行中的更改)

回答by RobertG

There is already a nice answer directly in the edited question that helped me a lot - thank you!

直接在编辑过的问题中已经有一个很好的答案对我有很大帮助 - 谢谢!

I just want to add a bit of curious behavior that I stumbled across: matching against a dollar sign at the end of lines (e.g. when modifying PS1 in your .bashrc file). As a workaround, I match for additional whitespace.

我只想添加一些我偶然发现的奇怪行为:匹配行尾的美元符号(例如,在修改 .bashrc 文件中的 PS1 时)。作为一种解决方法,我匹配额外的空格。

$ DOLLAR_TERMINATED="123456 $"
$ echo "${DOLLAR_TERMINATED}" | sed -e "s/ \$/END/"
123456END
$ echo "${DOLLAR_TERMINATED}" | sed -e "s/ \$$/END/"
sed: -e expression #1, char 13: Invalid back reference
$ echo "${DOLLAR_TERMINATED}" | sed -e "s/ \$\s*$/END/"
123456END

Explanation to the above, line by line:

对上面的逐行解释:

  • Defining DOLLAR_TERMINATED - I want to replace the dollar sign at the end of DOLLAR_TERMINATED with "END"
  • It works if I don't check for the line ending
  • It won't work if I match for the line ending as well (adding one more $ on the left side)
  • It works if I additionally match for (non-present) whitespace
  • 定义 DOLLAR_TERMINATED - 我想用“END”替换 DOLLAR_TERMINATED 末尾的美元符号
  • 如果我不检查行尾,它会起作用
  • 如果我也匹配行尾,它将不起作用(在左侧再添加一个 $)
  • 如果我另外匹配(不存在的)空格,它会起作用

(My sed version is 4.2.2from February 2016, bash is version 4.3.48(1)-release (x86_64-pc-linux-gnu), in case that makes any difference)

(我的 sed 版本是4.2.22016 年 2 月的,bash 是版本4.3.48(1)-release (x86_64-pc-linux-gnu),以防万一)