3

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

3 Answers 3

6

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-/'
1
  • Bravo!!! This is a more comprehensive answer than the first one I chose. and I learned a new trick for sed too! I'll choose this as the answer once the vote overtakes the first one. Also, reiterate my comment on the first one, although OP uses a simple example, "I do need to match first, then replace $1 and $2 in my real case." for performance concerns (if the matching first can dramatically reduce the replace operation, 0.1% vs 100%). thx!
    – xpt
    Jun 11, 2022 at 20:58
6

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-/; }
1
  • 1
    Thanks for providing the solution for the more general case, as I do need to match first, then replace $1 and $2 in perl in my real case.
    – xpt
    Jun 11, 2022 at 18:03
1

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

3
  • As I've repeatedly commented in the previous replies, although OP uses a simple example, "I do need to match first, then replace $1 and $2 in my real case.". As for the identical digits, it's all there for $1 and $2 matching. If you could give a simple example that doesn't have identical digits but still able to provide $1 and $2 matching, and requires equal or less steps in preparation than OP, I'll be glad to hear that. thx.
    – xpt
    Jul 12, 2022 at 2:51
  • I've added an extra example, in classic 'pattern-action' format (third from the top). Hopefully you find it useful. Jul 12, 2022 at 3:47
  • thanks, already up-voted. Hmm... I was asking for providing the sample test data, and the "match first, then replace" was only the reason for that (and not the focus) though.
    – xpt
    Jul 12, 2022 at 12:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .