【问题标题】:Simple search and replace without regex没有正则表达式的简单搜索和替换
【发布时间】:2011-12-19 17:16:28
【问题描述】:

我有一个包含各种通配符的文件,我希望能够从 (Bash) shell 脚本中替换它。在其中一个变量包含正则表达式特有的字符之前,我有以下效果很好:

VERSION="1.0"
perl -i -pe "s/VERSION/${VERSION}/g" txtfile.txt    # No problems here

APP_NAME="../../path/to/myapp"
perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt  # Error!

所以我想要一些只执行文字文本替换而不是正则表达式的东西。是否有任何使用 Perl 或其他工具的简单单行调用可以做到这一点?

【问题讨论】:

  • 我认为答案是 “不,perl 中没有文字字符串的字符串替换”。您必须使用正则表达式,如果您希望正则表达式为文字,则必须使用quotemeta\Q \E,如perldoc quotemeta中所述
  • 对于那些从搜索引擎结果中寻找方法来避免正则表达式(例如,出于性能原因)的人,glenn jackman's answerJess's answer 使用 substr()index() 来实现这一点。

标签: perl bash text


【解决方案1】:

执行此操作的“正确”方法是转义 shell 变量的内容,这样它们就不会被视为特殊的正则表达式字符。您可以在 Perl 中使用 \Q 执行此操作,如

s/APP_NAME/\Q${APP_NAME}/g

但是当从 shell 脚本调用时,反斜杠必须加倍以避免丢失,就像这样

perl -i -pe "s/APP_NAME/\\Q${APP_NAME}/g" txtfile.txt

但我建议用 Perl 编写整个脚本会容易得多

【讨论】:

  • 浏览你的一些正则表达式答案——很高兴吸收别人的正则表达式风格。 :)
  • 使用APP_NAME="../../path/to/myapp"(来自问题),由于变量中的/ 终止了正则表达式的替换部分,此命令失败。有通用方法吗?
  • @TomFenech:六年过去了,我对自己不太自信!问题是,在使用 bash 的 Perl 单行代码中,有两个级别的转义正在进行。首先,bash 将处理字符串 perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt 并应用它自己的任何插值,然后 perl 将处理结果并依次执行相同的操作。我现在离 PC 还很远,但我认为这应该解决。
  • 当我需要参数化包含中间分隔符/s/.../.../g 的字符串时,这甚至可以工作。我做了类似perl -i -p0e 's/blah\Q${variable}/s' 之类的操作(注意,这缺少分隔/,但是那是在我的变量中,我从带有while 的文件中传递。
【解决方案2】:

使用以下内容:

perl -i -pe "s|APP_NAME|\\Q${APP_NAME}|g" txtfile.txt

由于作为路径一部分的竖线不是合法字符,因此您可以继续使用。

【讨论】:

  • 太好了,成功了!我忘记了正则表达式本身不是问题,而是整个替换命令,因此更改分隔符有效。
  • 在 Unix 上,| 是一个有效的路径字符。事实上,除了NUL (\0) 之外的所有字符在 Unix 的文件名中都是有效的(如果不常见的话)。这就是为什么你应该简单地使用内置的报价机制,正如 Borodin 下面所建议的那样。
  • @the_mandrill:功能性是衡量最佳解决方案的一个糟糕指标。
  • 在变量的情况下使用str =~ s/\Q$replace_this\E/$with_this/;
【解决方案3】:

我不是特别喜欢这个答案,因为应该有更好的方法在 Perl 中进行文字替换。 \Q 是神秘的。使用quotemeta 会增加额外的代码行。

但是...您可以使用substr 替换字符串的一部分。

#!/usr/bin/perl
my $name = "Jess.*";
my $sentence = "Hi, my name is Jess.*, dude.\n";
my $new_name = "Prince//";
my $name_idx = index $sentence, $name;
if ($name_idx >= 0) {
    substr($sentence, $name_idx, length($name), $new_name);
}
print $sentence;

输出:

Hi, my name is Prince//, dude.

【讨论】:

  • 你的帖子被严重低估了。谢谢先生从那些正则表达式中拯救我!有时,您只需要除正则表达式之外的其他东西来替换内容(例如,在替换字符串中的正则表达式时)。
  • @user1834095: "\Q 是神秘的。使用quotemeta 会增加额外的代码行"。但只要程序写得好,两者都不是问题。正则表达式是 bash 提供的文件 glob 的自然扩展,它们同样神秘,但只是生活的一部分。我同意 substr 作为 lvalue 是一个有用的习惯用法,但 index 已经过时了,尤其是现在我们有 @-@+。所有这一切都在 Perl 6 中得到了极大的改进!
【解决方案4】:

您不必为此使用正则表达式(使用 substr()index()length() ):

perl -pe '
  foreach $var ("VERSION", "APP_NAME") {
    while (($i = index($_, $var)) != -1) {
      substr($_, $i, length($var)) = $ENV{$var};
    }
  }
'

确保你export你的变量。

【讨论】:

  • 好的,但是,我真的想知道从性能的角度来看,for + while + substr + length 是否真的比简单的替换更好......
  • 基准测试可以回答这类问题。试一试,计时,然后看看。
  • 无需测试我已经知道答案的东西 ;) 当然,您的解决方案并不比简单的替换更好,至少使用 Perl ;)
【解决方案5】:

您可以使用正则表达式,但可以转义任何特殊字符。

这样的事情可能会奏效。

APP_NAME="../../path/to/myapp"
APP_NAME=`echo "$APP_NAME" | sed -e '{s:/:\/:}'`
perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt

【讨论】:

    【解决方案6】:

    用途:

     perl -i -pe "\$r = qq/\Q${APP_NAME}\E/; s/APP_NAME/\$r/go"
    

    理由:Escape sequences

    【讨论】:

      【解决方案7】:

      我设法找到了一个可行的解决方案,部分基于其他人的答案:

      app_name='../../path/to/myapp'
      perl -pe "\$r = q/${app_name//\//\\/}/; s/APP_NAME/\$r/g" <<<'APP_NAME'
      

      这会根据 shell 参数扩展的结果创建一个 Perl 变量 $r

      ${app_name//\//\\/}
      
      ${            # Open parameter expansion
      app_name      # Variable name
      //            # Start global substitution
      \/            # Match / (backslash-escaped to avoid being interpreted as delimiter)
      /             # Delimiter
      \\/           # Replace with \/ (literal backslash needs to be escaped)
      }             # Close parameter expansion
      

      所有这些工作都是为了防止变量内的正斜杠被视为 Perl 语法,否则会关闭字符串周围的 q// 引号。

      在替换部分,使用变量$r$被转义,以防止它被视为双引号内的shell变量)。

      测试一下:

      $ app_name='../../path/to/myapp'
      $ perl -pe "\$r = q/${app_name//\//\\/}/; s/APP_NAME/\$r/g" <<<'APP_NAME'
      ../../path/to/myapp
      

      【讨论】:

        猜你喜欢
        • 2013-06-14
        • 2018-05-25
        • 2010-11-25
        • 2010-10-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多