如何在Bash脚本中将正则表达式作为参数传递给Perl one-liner?

4

我有一个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 one-liner的包装器(并且还要做一些其他事情),然后使用正则表达式作为它的参数调用它。我编写了以下脚本:

#!/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 one-liner中的第一个捕获组之间的冲突。因此,我创建了第二个函数rega,它首先将该表达式分配给另一个变量,然后再将其传递给Perl。但输出与前一个函数相同。

那么,我如何在函数内部将正则表达式传递给Perl one-liner?我做错了什么?


2
当您将双引号放在函数内部时会发生什么?(即编写 perl -ne "s/($1)/**\1**/&&print" - Aserre
你可以使用 sed 更高效地完成相同的事情。请参考 simbabque 的答案如何引用它。 - Peter Cordes
@Ploutox 使用双引号解决了这个问题。在我之前的测试中,我认为需要使用双引号进行变量扩展,但是它导致了一些意外的结果。现在一切都好了。我需要做更多的测试来找出之前的问题是什么。 - Rafal
1
@PeterCordes 我不能使用 sed,因为我正在使用 perl 正则表达式,其中一些直接在 sed 中无法工作。由于我还在文本编辑器中手动处理它们,将表达式移植到 sed 中是一个可以跳过的额外步骤。但还是谢谢你的建议。 - Rafal
2个回答

4

因为 shell 不会在单引号中解析变量 ',所以你需要使用双引号"。这也在this answer中有详细的解释。

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

此外,在Perl中,正则表达式捕获组最终会出现在$1$2等变量中,而不是\1。如果你打开警告信息(使用-w参数),你将会收到一个\1 better written as $1的警告信息。这在perldiag中有解释。

\%d better written as $%d

(W syntax) 在模式之外, 回溯引用作为变量存在。在替换操作的右侧,反斜杠的使用是被允许的,但从风格上讲,最好使用变量形式,因为其他Perl程序员会期望它,并且如果有超过9个回溯引用,使用变量形式更加有效。

(W syntax)意味着你可以使用no warnings 'syntax';关闭此警告信息。


当我使用这个进行测试时,它是可以工作的。但你一定需要双引号。 - simbabque
1
在写评论之前,我进行了测试,但是我检查的是另一个函数的输出文件,这使我错误地得出结论认为你的解决方案不起作用。在写完第一条评论后,我发现了这一点,因此将其删除了。 你的解决方案可以工作,这很棒。但就我所看到的而言,这是由于双引号(可以扩展变量),而不是使用\1替换$1 - Rafal
可能吧。不过@Сухой27也说得对,使用$1\1会产生警告。 - simbabque
1
我认为应该在答案顶部添加关于双引号的信息,因为这是问题的根源。可以作为附注添加有关“\1”与“$1”的信息,并解释为什么那种方式更好。现在的回答表明“\1”是一个问题,但实际上并不是。因此,我不愿接受解决问题的答案,而是建议其源头实际上在别处。 - Rafal
非常感谢,这解释得很清楚。 - Rafal
显示剩余3条评论

2

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

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 开启警告,它会告诉您在 s/// 的替换部分中 \1 最好改写为 $1


1
你的回答有什么优势,相比于使用双引号而不是单引号(这也可以解决问题)? - Rafal
1
@Rafal 是的,双引号很糟糕,因为它们还会插入你不想要的变量。 - mpapec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接