Rafal
Rafal

Reputation: 894

How can I pass a regular expression as a parameter to a Perl one-liner in a Bash script?

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

I want 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 the 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?'

The output of the 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?

Upvotes: 4

Views: 1749

Answers (2)

mpapec
mpapec

Reputation: 50637

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

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

Using the %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 is better written as $1 for the substitution part of s///.

Upvotes: 2

simbabque
simbabque

Reputation: 54323

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';

Upvotes: 4

Related Questions