How to pass a regular expression as a parameter to

2019-06-26 07:40发布

问题:

I have this input.txt file:

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

I want to search for matches of the runs? regular expression and output them to a file, while highlighting matches with two asterisks on both sides of the match. So my desired output is this:

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

What I want to do is to write a function that would be a wrapper for this perl one-liner (and it would do few other things) and then invoke it with a regular expression as its parameter. I wrote following script:

#!/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?'

Output of first perl one-liner is what I want. But when I try to wrap it in a reg function and pass the expression as a parameter, instead of desired output I get:

****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

I thought the issue was some conflict between $1 as a function parameter and the first capturing group in the perl one-liner. So I created a second function rega which first assigns that expression to a different variable and only then passes it to perl. But the output is the same as previous function.

So, how can I pass a regular expression to a perl one-liner inside the function? What I am doing wrong?

回答1:

You need to use double-quotes " because the shell does not interpolate variables in single-quotes '. This is also nicely explained in this answer.

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

Furthermore, in Perl the regex capture groups end up in $1, $2 and so on. Not in \1. If you turn on warnings (with -w in your one-liner) you will get a \1 better written as $1 warning. It is explained in perldiag.

\%d better written as $%d

(W syntax) Outside of patterns, backreferences live on as variables. The use of backslashes is grandfathered on the right-hand side of a substitution, but stylistically it's better to use the variable form because other Perl programmers will expect it, and it works better if there are more than 9 backreferences.

The (W syntax) means that you can turn this warning off with no warnings 'syntax';



回答2:

You can pass $1 regex as command line parameter, and compile it with qr// as single quotes for perl script don't interpolate under shell,

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

using %ENV environment variable

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

and as a side note, if you enable warnings with -w it will tell you that \1 better written as $1 for the substitution part of s///