【问题标题】:How can I pass a regular expression as a parameter to a Perl one-liner in a Bash script?如何将正则表达式作为参数传递给 Bash 脚本中的 Perl 单行器?
【发布时间】:2015-10-12 00:09:21
【问题描述】:

我有这个input.txt 文件:

Dog walks in the park
Man runs in the park
Man walks in the park
Dog runs in the park
Dog stays still
They run in the park
Woman runs in the park

我想搜索 runs? 正则表达式的匹配项并将它们输出到文件中,同时在匹配项的两侧用两个星号突出显示匹配项。所以我想要的输出是这样的:

Man **runs** in the park
Dog **runs** in the park
They **run** in the park
Woman **runs** in the park

我想编写一个函数来包装这个 Perl 单行器(它会做一些其他事情),然后使用正则表达式作为参数调用它。我写了以下脚本:

#!/bin/bash

function reg {
    perl -ne 's/($1)/**\1**/&&print' input.txt > regfunctionoutput.txt
}

function rega {
    regex="$1"
    perl -ne 's/($regex)/**\1**/&&print' input.txt > regafunctionoutput.txt
}

perl -ne 's/(runs?)/**\1**/&&print' input.txt > regularoutput.txt
reg 'runs?'
rega 'runs?'

第一个 Perl 单行的输出就是我想要的。但是,当我尝试将其包装在 reg 函数中并将表达式作为参数传递时,我得到的不是所需的输出:

****Dog walks in the park
****Man runs in the park
****Man walks in the park
****Dog runs in the park
****Dog stays still
****They run in the park
****Woman runs in the park

我认为问题在于 $1 作为函数参数与 Perl 单行中的第一个捕获组之间存在一些冲突。所以我创建了第二个函数rega,它首先将该表达式分配给不同的变量,然后才将其传递给Perl。但是输出和之前的函数一样。

那么,如何将正则表达式传递给函数内部的 Perl 单行器?我做错了什么?

【问题讨论】:

  • 在函数中加上双引号会发生什么? (即写perl -ne "s/($1)/**\1**/&&print"
  • 您可以使用sed 更有效地执行相同的操作。有关如何引用它,请参阅 simbabque 的答案。
  • @Ploutox 使用双引号解决了这个问题。在我之前的测试中,我假设我需要使用双引号来进行变量扩展,但这会导致一些意想不到的结果。现在一切都很好。我需要做更多的测试才能更早地找出问题所在。
  • @PeterCordes 我不能使用 sed,因为我使用的是 perl 正则表达式,其中一些不能直接使用 sed。由于我还在文本编辑器中对它们进行了一些手动操作,因此将表达式移植到 sed 是可以跳过的附加步骤。不过谢谢你的建议。

标签: regex bash perl


【解决方案1】:

您可以将 $1 正则表达式作为命令行参数传递,并使用 qr// 作为单引号编译它,因为 Perl 脚本不会在 shell 下插入,

perl -ne '
  BEGIN{ ($re) = map qr/$_/, shift @ARGV }
  s/($re)/**\1**/ && print
' "$1" input.txt > regfunctionoutput.txt

使用%ENV 环境变量:

perl -ne '
  BEGIN{ ($re) = map qr/$_/, $ENV{1} }
  s/($re)/**\1**/ && print
' input.txt > regfunctionoutput.txt

附带说明,如果您使用-w 启用警告,它会告诉您\1 is better written as $1s/// 的替换部分。

【讨论】:

  • 你的答案是否比使用双引号而不是单引号有任何优势(这也解决了问题)?
  • @Rafal 是的,双引号很糟糕,因为它们还会插入您不想插入的变量。
【解决方案2】:

您需要使用双引号 ",因为 shell 不会在单引号中插入变量 'this answer 也很好地解释了这一点。

function reg {
    perl -ne "s/($1)/**\$1**/g&&print" input.pl > regfunctionoutput.txt
}

此外,在 Perl 中,正则表达式捕获组以 $1$2 等结尾。不在\1。如果您打开警告(在您的单行中使用 -w),您将收到一个 \1 更好地写为 $1 警告。在perldiag中有解释。

\%d 最好写成 $%d

(W 语法)在模式之外, 反向引用作为变量存在。反斜杠的使用是 祖父在替换的右侧,但是 风格上最好使用变量形式,因为其他 Perl 程序员会期待它,如果有超过 9 个反向引用。

(W 语法) 表示您可以使用 no warnings 'syntax'; 关闭此警告

【讨论】:

  • 当我用这个测试它时,它起作用了。不过你肯定需要双引号。
  • 在写我的评论之前,我测试了它,但是我检查了不同的输出文件,它来自另一个函数,导致我得出错误的结论,即你的解决方案不起作用。我在写了我的第一条评论后发现,所以我删除了它。 您的解决方案有效,这很棒。但据我所知,这是因为双引号(扩展变量),而不是因为使用$1 而不是\1 进行替换。
  • 可能。尽管@Сухой27 对$1\1 的对比也是正确的,这会发出警告。
  • 我将其添加到答案中。
  • 我认为应该在答案的顶部添加有关双引号的信息,因为这确实是问题的根源。有关\1$1 的信息以及为什么这种方式真的更好的解释可以作为旁注添加。照原样,它表明\1 在这里是一个问题,但事实并非如此。所以我不愿意接受解决问题的答案,但建议它的来源在其他地方而不是现实。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-10
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多