Conditional replacement using perl

Very simple hypothetical question,

I’ve reached the limitation of sed and need to change my sed script to perl. So for sed conditional replacement of

sed '/condition/ s/xx/yy/'

How to do it in perl?

For e.g. how to do the following in perl?

seq 6 > /tmp/tf

$ paste -d '' /tmp/tf /tmp/tf | sed -E '/[135]/s/^(.)(.)$/1.2-/'
1.1-
22
3.3-
44
5.5-
66

$ paste -d '' /tmp/tf /tmp/tf | perl -pe 's/$&/$1.$2-/ if /^([135])(.)$/'
.-
22
.-
44
.-
66
Asked By: xpt

||

The problem is that $1 and so on are reset on each new regular expression, so they will be empty unless you repeat them in the s command, i.e.

s/^([135])(.)$/$1.$2-/ if /^([135])(.)$/'

which can simplified by removing the if:

s/^([135])(.)$/$1.$2-/

In a more general case, you can preserve the captured values:

if (/^([135])(.)$/) { $a=$1; $b=$2; s/$&/$a.$b-/; }
Answered By: meuh

If you aim to reduce typing and to increase the similarity between how to do this in Perl and sed. In that case, both sed and Perl allow the reuse of the most recently matched regular expression by specifying an empty expression.

sed '/^([135])(.)$/ s//1.2-/'
perl -pe '/^([135])(.)$/ && s//$1.$2-/'

The empty regular expression in the s/// command will reuse the expression from the preceding test (in general, the most recent matching expression).

In Perl, we must add && between the test and the substitution to let it act as a short-circuit if-statement. In sed, the first expression simply acts as the address of the substitution command.

In general,

sed '/condition/ s/xx/yy/'

… would be "the same as" (taking slightly different syntax and regular expression flavors into account)

perl -pe '/condition/ && s/xx/yy/'

In this case, though, it would be simpler just to apply the substitution directly:

sed 's/^([135])(.)$/1.2-/'
perl -pe 's/^([135])(.)$/$1.$2-/'
Answered By: Kusalananda

Using Raku (formerly known as Perl_6)

raku -pe 's/^ (<[135]>) (.) $/$0.$1-/;'  

OR

raku -pe 's/^ (.) (.) $/$0.$1-/ if  m/^ <[135]> /;'  

#Immediately above being a re-arrangement of:

raku -pe 'if m/^ <[135]> /  {s/^ (.) (.) $/$0.$1-/};' 

Posting this in the hopes that it will prove useful for Perl users who dabble in Raku. In Raku, capturing starts from $0, and character classes are created with <[]> markers (square brackets alone are reserved for grouping). Also matching is generally insensitive to whitespace (i.e. Perl5’s x is the default).

Trying @Kusalananda’s first Perl5 code example (with a Raku ‘accent’ as described) will produce the Raku error Null regex not allowed. Hence the first Raku answer above looks much more like @Kusalananda’s last Perl5 code example. (The second Raku answer above utilizes an if conditional, which some users may find more readable).

Sample Input:

11
22
33
44
55
66

Sample Output:

1.1-
22
3.3-
44
5.5-
66

ADDENDUM: Since the OP posted sample data that concatenated identical digits–I am wondering if there is still an unanswered question here regarding backreferences. In Raku if you want to reuse the first capture within a match (e.g. left side of s/// operator), you do the following:

raku -pe 's/^ (<[135]>) $0 $/$0.$0-/;'  

OR

raku -pe 's{^ (<[135]>) $0 $} = "$0.$0-";'  

OR (named captures, below):

raku -pe 's/^ $<myOdd>=<[135]> $<myOdd> $/$<myOdd>.$<myOdd>-/;' 

OR

raku -pe 's{^ $<myOdd>=<[135]> $<myOdd> $} = "$<myOdd>.$<myOdd>-";'  

The second and fourth examples immediately above use Raku’s new "substitute-assignment" notation, which (to enhance readability) also allows various delimiters–brackets, curlies, etc. (Of course, a separate if conditional could be used as denoted in the earlier section, but that might result in less-readable code).

https://raku.org/archive/rfc/144.html
https://raku.org/archive/rfc/331.html
https://raku.org

Answered By: jubilatious1
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.