Home > Software design >  Use nested perl command to replace part of line
Use nested perl command to replace part of line

Time:09-27

I have two perl commands and I want to apply one of the commands to replace a capture group in the other command.

Command 1 converts a CamelCase string to snake_case. It works as expected:

echo 'AbcDefGhi' \
  | perl -p -E 's/(?:\b|(?<=([a-z])))([A-Z][a-z] )/(defined($1) ? "_" : "") . lc($2)/eg'

Result:

abc_def_ghi

Command 2 finds the CamelCase strings that need to be replaced. It works fine, too (replacing with XXX$1YYY here):

echo '  <div id="AbcDefGhi">' \
  | perl -p -E 's#  <div id="([^"]*)">#<div id="XXX$1YYY">#s'

Result:

  <div id="XXXAbcDefGhiYYY">

But how would I combine both commands now? I tried a lot of stuff, but can't get it to work. My best, probably very bad, attempt was to use the e modifier in command 1 and then printf "$1" and pipe that into command 2, but then I think it accessed the wrong $1 from the outside perl command.

Ideally I want to have it work something like this:

echo '  <div id="AbcDefGhi">' | perl -p -E 's#  <div id="([^"]*)">#MAGIC#s'

Result:

  <div id="abc_def_ghi">

Solutions with other tools are welcome too, but I would really like to improve to perl knowledge.

CodePudding user response:

So basically one one-liner is to locate the correct string, and the other to change that string. Well, you're already using the /e modifier, so why not just insert the substitution in there?

perl -p -E 's#  <div id="([^"]*)">#
    qq(<div id=") . 
    $1 =~ s/(?:\b|(?<=([a-z])))([A-Z][a-z] )/(defined($1) ? "_" : "") . lc($2)/egr .
    qq(">)#sex'

Basically we did this:

perl -pE' s# .... # s/...// # '

It's a bit of a mouthful to read, so I split it up using the /x modifier, but you can just remove that and put it on a single line.

Note that I added the /r modifier, because we don't want to change $1 (we can't, it's read-only), we just want the value after substitution. And that is what /r does, perform the substitution and return the string, but leave original string unchanged.

Luckily you had also already change delimiters on s/// so we don't even need to do that.

This is what the line would look like, for copy/paste purposes:

perl -p -E 's#  <div id="([^"]*)">#qq(<div id=") . $1 =~ s/(?:\b|(?<=([a-z])))([A-Z][a-z] )/(defined($1) ? "_" : "") . lc($2)/egr . qq(">)#se'

I'm sure the better way is to write a simpler command, but this is the quick way to merge your commands.

  •  Tags:  
  • perl
  • Related