退出美元符号regexp for sed

时间:2016-02-19 13:18:45

标签: 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

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

rm -f var/lib/tomcat7/webapps/ROOT/WEB-INF/classes/ExampleFile$3$1$1$1$2$1$1.class

执行此脚本不会删除所需文件,因为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

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

修改

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

  1. 使用内容test
  2. 创建文本文件bla$bla
  3. cat test提供bla$bla
  4. cat test | sed "s/$/2/g"提供bla$bla2
  5. cat test | sed "s/\$/2/g"提供bla$bla2
  6. cat test | sed "s/\\$/2/g"提供bla2bla
  7. 因此,最后一个版本就是答案。请记住:在测试时,首先确保您的测试是正确的,然后再询问测试对象........

3 个答案:

答案 0 :(得分:4)

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

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

或使用printf,这使得引用更好,更便携:

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

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

答案 1 :(得分:3)

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

cat filenames.txt | sed "s/\\$/\\\\$/g" > escaped-filenames.txt
是的,这是连续四个反斜杠。这会创建所需的更改: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

解决背景问题的替代方法

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

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

答案 2 :(得分:0)

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

我只是想添加一些我偶然发现的好奇行为:在行尾(例如在.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

上述说明,逐行说明:

  • 定义DOLLAR_TERMINATED - 我想将DOLLAR_TERMINATED末尾的美元符号替换为" END"
  • 如果我不检查结束行
  • ,它会起作用
  • 如果我也匹配结尾的行(在左侧再添加一个$),它将无法工作。
  • 如果我另外匹配(不存在的)空格
  • ,它会起作用

(我的sed版本从2016年2月开始为4.2.2,bash为版本4.3.48(1)-release (x86_64-pc-linux-gnu),如果有任何差异的话)