逃脱美元符号为sed的regexp

Cap*_*hab 8 regex bash shell sed

我会在实际询问之前介绍我的问题 - 随意跳过本节!

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

要在软件系统中手动更新文件,我正在创建一个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
Run Code Online (Sandbox Code Playgroud)

这很好用.但是,显然我的文件通常在文件名中有一个美元符号($),这是由于GWT框架的一些排列.以下是上面创建的bash脚本中的一个示例行:

rm -f var/lib/tomcat7/webapps/ROOT/WEB-INF/classes/ExampleFile$3$1$1$1$2$1$1.class
Run Code Online (Sandbox Code Playgroud)

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

我的实际问题

我现在想在上述管道中添加一个sed-Command,替换这个美元符号.事实上,sed还将美元符号作为正则表达式的特殊字符读取,因此显然我也必须将其转义.但不知怎的,这不起作用,我在google搜索后找不到解释.

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

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
Run Code Online (Sandbox Code Playgroud)

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

编辑

我刚才意识到,上面的例子开头是错误的 - echo命令已经将$解释为变量,而下面的sed无论如何也得不到它......这里有一个恰当的例子:

  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

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

Cap*_*hab 7

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

cat filenames.txt | sed "s/\\$/\\\\$/g" > escaped-filenames.txt
Run Code Online (Sandbox Code Playgroud)

是的,这是连续四个反斜杠。这将创建所需的更改:文件名将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
Run Code Online (Sandbox Code Playgroud)

替代解决背景问题

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • 不 - 在 shell 中的双引号字符串中转义美元符号的正确方法是添加两个反斜杠。在单引号中,一个反斜杠是正确且足够的,两个反斜杠是错误的。通常,除非您需要 shell 插入变量并执行命令替换,否则请使用单引号。尽可能使用单引号。 (2认同)

che*_*ner 4

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

echo "rm -f '$i'" >> REMOVEOLDFILES.sh
Run Code Online (Sandbox Code Playgroud)

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

printf "rm -f '%s'" "$i" >> REMOVEOLDFILES.sh
Run Code Online (Sandbox Code Playgroud)

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