1

This is a follow-up to a previous question I had asked. For that question, I received an excellent answer by @markp-fuso demonstrating the use of an array of search patters for sed to perform a particular substitution task.

In this question, I would like to perform the same find and replace task of replacing all forms of pow(var,2) with square(var) but I would like to do this by using a regex variable.

The sample input and output files are below:

InputFile.txt:

pow(alpha,2) + pow(beta,2)
(3*pow(betaR_red,2))
2/pow(gammaBlue,3))
-pow(epsilon_gamma,2)+5

OutputFile.txt:

square(alpha) + square(beta)
(3*square(betaR_red))
2/pow(gammaBlue,3))
-square(epsilon_gamma)+5

After looking at this answer and some more tinkering on https://regex101.com/r/80Hp5Z/1/, I was able to describe the text between pow( and ,2) using (?<=pow\().*?(?=,2\)).

Using Bash, I am trying to form a two command solution where:

  1. First command sets var to (?<=pow\().*?(?=,2\))
  2. Second command performs sed "s/pow(${var},2)/square(${var})/g" InputFile.txt > OutputFile.txt

It seems once I figure out how to set var successfully in Step 1, I can proceed with the command in Step 2. However, after reading this question and this question I tried the following two commands but they did not perform any substitutions:

bash$ var="(?<=pow\().*?(?=,2\))"
bash$ sed "s/pow(${var},2)/square(${var})/g" InputFile.txt > OutputFile.txt

I would really appreciate some help forming a 2-command Bash solution as described above that makes use of a regex variable and transforms InputFile.txt into OutputFile.txt.

procyon
  • 31
  • 7
  • 1
    Probably a much better solution overall is to write your program in such a way that you can switch between these functions without changing the code, either via a `#define` (which obviously requires you to recompile the program, but is very easy to manage from a Makefile or similar) or even by rewriting the program so you can switch at run-time (via a command-line option then, or perhaps an environment variable). – tripleee Oct 03 '20 at 20:15
  • @tripleee: Thanks, this idea is very interesting and I didn't know you can do this. If possible, could you please give me some tips or references on how I can write such a `#define` statement that allows seamless switching between `pow(var,2)` and `square(var)`? – procyon Oct 03 '20 at 20:25
  • How to replace `pow(foo(),2)` would have been a far more interesting question since matching one `)` is trivial but matching an unknown number of nested `(...)`s isn't. – Ed Morton Oct 03 '20 at 21:02
  • 1
    Create a simple wrapper function which accepts one argument `var`, then returns either `pow(var, 2)` or `square(var)` depending on your `#define`. Post a new question if you can't figure it out (though I would guess it has been asked before; search before asking). – tripleee Oct 04 '20 at 09:14
  • Ah, I see what you are trying to say. This is really cool and I can see myself using this in other situations as well. Thanks @tripleee. – procyon Oct 04 '20 at 19:13

2 Answers2

2

If you want to deal with parentheses in squared expressions, try:

One-line Solution:

perl -pe 's/pow\s*\((([^()]|\((?2)+\))+)\s*,\s*2\s*\)/square($1)/g' inputFile.txt

Two-Line Solution:

REGEX="pow\s*\((([^()]|\((?2)+\))+)\s*,\s*2\s*\)"
perl -pe "s/$REGEX/square(\1)/g" inputFile.txt

https://regex101.com/r/Dp0Duf/1.

procyon
  • 31
  • 7
Alexander Mashin
  • 3,892
  • 1
  • 9
  • 15
  • Alexander Mashin, thanks a lot for the `perl` solution, it works perfectly. Would it be possible to make this into a two-command solution, where the first command sets `var =` a regex and then in the second command, this regex variable gets used in the `perl` command? – procyon Oct 03 '20 at 18:59
  • @procyon, create a file called, say, `t`: `#!/bin/bash REGEX="pow\s*\((([^()]|\((?2)+\))+)\s*,\s*2\s*\)" perl -pe "s/$REGEX/square($1)/g" $1` Make it executable and call it like this: `./t file`. **P.S.** The line breaks should be before `REGEX` and `perl`. – Alexander Mashin Oct 03 '20 at 19:28
  • Thanks, I tried out your bash script using `./t file`, but it converts `pow(alpha,2)` to `square(file)` instead of `square(alpha)`. Could you please tell me why this happens when it looks equivalent to your one-liner after variable expansion? – procyon Oct 03 '20 at 19:49
  • 1
    Oh, sorry, it's funny. Two `$1`'s are mixed up. Try this: `#!/bin/bash REGEX="pow\s*\((([^()]|\((?2)+\))+)\s*,\s*2\s*\)" perl -pe "s/$REGEX/square(\1)/g" $1` – Alexander Mashin Oct 03 '20 at 19:54
  • Thanks so much, Alexander Mashin. I've accepted your answer and added your updated bash script as a two-line bash solution to your answer as a suggested edit. I sincerely appreciate your help. – procyon Oct 03 '20 at 20:07
1

You may use this sed with one capture group:

s='pow(\([^)]+),2\)'
sed -E "s/$s/square\1)/g" file
square(alpha) + square(beta)
(3*square(betaR_red))
2/pow(gammaBlue,3))
-square(epsilon_gamma)+5

RegEx Demo

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Thanks, I tried this and it works! I'm trying to use `(?<=pow\().*?(?=,2\))` (https://regex101.com/r/80Hp5Z/1/). Would it be possible to use that in a sed substitution command to get the same result? – procyon Oct 03 '20 at 18:02
  • 2
    No, you cannot use lookahead and lookbehind in `sed`. – anubhava Oct 03 '20 at 18:06
  • Thanks @anubhava. One of the things I am confused about in my question is how to first set the regex part of a `sed` command as a variable and then use it later on in the actual `sed` command. Would it be possible to add a two-liner solution to your answer where the first command sets `var` to a regex and in the second command `var` gets used by `sed` to perform the substitution? – procyon Oct 03 '20 at 18:16
  • Sorry I went to sleep after previous comment. I have updated my answer. – anubhava Oct 04 '20 at 04:05
  • 1
    Your two step solution will work really well with a workflow involving 1) tinkering on regex101.com and setting a regex variable 2) applying the sed command. Thanks @anubhava, I appreciate your help! – procyon Oct 04 '20 at 19:02