【问题标题】:Why is the escape of quotes lost in this regex substitution?为什么在这个正则表达式替换中引号的转义丢失了?
【发布时间】:2019-02-27 02:33:52
【问题描述】:

为什么双引号的转义在这种情况下会丢失?

$ cat foo.txt   
This is a \"very good\" text worth AMOUNT dollars    
$ cat full_story.txt   
This is about money:  
STORY  

使用以下方法对其进行测试:

VAR=$(cat foo.txt)                                                                                         
TOTAL=$(cat full_story.txt)  
echo "$TOTAL" | perl -pe "s/STORY/$VAR/g"  

结果:

This is about money:  
This is a "very good" text worth AMOUNT dollars  

双引号的转义丢失了。我期待:

This is about money:  
This is a \"very good\" text worth AMOUNT dollars  

我怎样才能保存逃逸?

【问题讨论】:

  • 加倍反斜杠或引用foo.txt 中的文本,如"This is .."
  • @keithpjolley:1) 引用 This is... 仍然会删除转义。这也不是我想要的,因为我没有引用那行 2)做双重转义可以解决这个问题,但我不想这样做,因为副本已经有转义
  • @keithpjolley:+1 双重转义虽然有效。我想了解为什么这种方法不起作用
  • 这只是用完了我脑海中最后一点点 perl 知识:echo "$TOTAL" | perl -pe '$v=q{'"$VAR"'};s/STORY/$v/g'。我确信有不同的方法可以更有意义。
  • 不要试图从 shell 生成 Perl 代码!你很可能做错了,而且确实你做错了。

标签: regex bash perl


【解决方案1】:

问题在于perl 会在显式替换字符串(不是perl 变量)中解析反斜杠转义,因此\" 会被解析为"。例如:

$ echo "A STORY" | perl -pe 's/STORY/\"Hello\"/'
A "Hello"

(注意 Bash 变量 $VAR 不会变成 perl 变量 $VAR ,而是一个常量字符串。)所以你需要像这样在常量字符串中转义反斜杠:

$ echo "A STORY" | perl -pe 's/STORY/\\"Hello\\"/' 
A \"Hello\"

您可以通过使用-s 切换到perl 将Bash 变量$VAR 转换为perl 变量$VAR 来解决此问题,如下所示:

echo "$TOTAL" | perl -spe 's/STORY/$VAR/g' -- -VAR="$VAR"

输出

This is about money:  
This is a \"very good\" text worth AMOUNT dollars

解释:

  • -s 启用perl 命令行上用户定义的开关的开关解析。在那里找到的任何开关都将从@ARGV 中删除,并在 Perl 程序中设置相应的变量。

【讨论】:

  • 另一种解决方法是说:echo "$TOTAL" | perl -pe "s'STORY'$VAR'g" 所以REPLACEMENT 被逐字解析。
  • @tshiono 谢谢,很好的观察。我已将您的建议编辑到我的答案中。
  • @HåkonHægland:非常有帮助!在-- -VAR="$VAR" 中为什么需要VAR 前面的破折号? IE。为什么不是-- VAR="$VAR"?我无法从您粘贴的文档中理解它应该是这样的
  • 是的,我也认为文档不是很清楚。有关详细信息,另请参阅this 答案。所以我认为需要用破折号将-VAR 和其他用户开关与普通perl 选项分开。我对其进行了一些测试,您甚至可以通过添加另一个 -- 实例来混合普通参数 (@ARGV),如下所示:perl -sE 'say ":ARGV=", join "|", @ARGV; say "VAR=$VAR"' -- -VAR=ss -- 1 2 3
  • @ikegami 好收获!那么使用-s 的第一种方法比不使用-s 的第二种方法更正确?
【解决方案2】:

您的代码存在code injection 错误。具体来说,您正试图从 shell 生成 Perl 代码,但失败了。

您将以下内容传递给 Perl:

s/STORY/This is a \"very good\" text worth AMOUNT dollars/g

您应该传递以下内容:

s/STORY/This is a \\"very good\\" text worth AMOUNT dollars/g

要正确生成 Perl 代码,您需要以下内容:

perl -pe's/STORY/'"$( printf %s "$VAR" | sed 's/\W/\\&/g' )"'/'

这会将以下内容传递给 Perl(也可以):

s/STORY/This is a \\\"very good\\\" text worth AMOUNT dollars/g

不过,一开始就避免生成 Perl 代码要简单得多。在不使用 STDIN 或外部存储的情况下向 Perl 传递信息的主要方式有三种。

  • 参数

    perl -pe'BEGIN { $VAR = shift(@ARGV) } s/STORY/$VAR/g' -- "$VAR"
    
  • 命令行选项

    在完整的程序中,您可以使用 Getopt::Long,但 perl -s 在这里也可以。

    perl -spe's/STORY/$VAR/g' -- -VAR="$VAR"
    
  • 环境变量

    VAR="$VAR" perl -pe's/STORY/$ENV{VAR}/g'
    

【讨论】:

  • 太棒了!一个问题:基本上,如果我在文本中有\\"very good\\",那么所有这些都是不必要的?
  • 所以你建议在你的数据文件中放置一段 Perl 代码——甚至不是一个完整的标记!这是自找麻烦,这是一个可怕的想法。我已经说过几次以下内容,但似乎没有深入了解:不要尝试从 shell 生成 Perl 代码!
  • 等等。你的意思是\\"very good\\" 是perl 代码?啊,我现在明白了。没看懂
  • 取决于VAR1 是正则表达式模式,还是要按字面意思匹配的文本字符串。很可能是后者,因此您刚刚引入了新的代码注入。您需要将文本转换为正则表达式:perl -pe's/\Q$ENV{VAR1}/$ENV{VAR2}/g'
  • 通过切换到双引号,您使 $ 对 shell 来说是特殊的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-24
  • 1970-01-01
相关资源
最近更新 更多