Complex Find and Replace in unix command line - regex

I'm trying to do a complex find and replace in all files through unix command line using the answer provided in find and replace in multiple files on command line but I can't seem to get my find and replace strings escaped properly.
I need to find all lines that contain:
if ($session['test']>0){
and replace it with
if ($session['test']>1){
The command I'm trying is:
find . -name '*.php' |xargs perl -pi -e 's/if\(\$session\[\'test\'\]\>0\){/if\(\$session\[\'test\'\]\>1\){/g'
But that gives me another prompt which requires me to enter an additional ' character because the command tells me there's a string that's missing termination. If I put it in extra ' at the end, I get:
Unmatched ) in regex; marked by <-- HERE in m/if\(\$session\[\test']>0) <-- HERE {/ at -e line 1.
How do I get this replacement to work?

Just use double quotes:
perl -pi -e "s/if\(\$session\[\'test\'\]\>0\){/if\(\$session\[\'test\'\]\>1\){/g"
Moreover, you are missing a space after if and the dollar sign needs to be escaped with three backslashes:
perl -pi -e "s/if \(\\\$session\[\'test\'\]\>0\){/if \(\\\$session\[\'test\'\]\>1\){/g"
^ ^^^ ^^^

Using double quotes and double escaping the $ works:
perl -pi -e "s/if \(\\\$session\['text'\]>0\){/if (\\\$session['text']>1){/"
A shorter but dirtier solution with a look-behind and look-ahead assertions:
perl -pi -e 's/(?<=if \(\$session\[.text.\]>)0(?=\){)/1/'

Related

Why does this regex work in grep but not sed?

I have two regular expressions:
$ grep -E '\-\- .*$' *.sql
$ sed -E '\-\- .*$' *.sql
(I am trying to grep lines in sql files that have comments and remove lines in sql files that have comments)
The grep command works using this regex; however, the sed returns the following error:
sed: -e expression #1, char 7: unterminated address regex
What am I doing incorrectly with sed?
(The space after the two hyphens is required for sql comments if you are unfamiliar with MySql comments of this type)
You're trying to use:
sed -E '\-\- .*$' *.sql
Here sed command is not correct because you're not really telling sed to do something.
It should be:
sed -n '/-- /p' *.sql
and equivalent grep would be:
grep -- '-- ' *.sql
or even better with a fixed string search:
grep -F -- '-- ' *.sql
Using -- to separate pattern and arguments in grep command.
There is no need to escape - in a regex if it is outside bracket expression (or character class) i.e. [...].
Based on comments below it seems OP's intent is to remove commented section in all *.sql files that start with 2 hyphens.
You may use this sed for that:
sed -i 's/-- .*//g' *.sql
The problem here is not the regex, the problem is that sed requires a command. The equivalent of your grep would be:
sed -n '/\-\- .*$/p'
You suppress output for non-matching lines -n ... you search (wrap your regex in slashes) and you print p (after the last slash).
P.S.: As Anub pointed out, escaping the hyphens - inside the regex is unnecessary.
You are trying to use sed's \cregexpc syntax where with \-<...> you are telling sed the delimiter character you want use is a dash -, but you didn't terminate it where it should be: \-<...>- also add d command to delete those lines.
sed '\-\-\-.*$-d' infile
see man sed about that:
\cregexpc
Match lines matching the regular expression regexp. The c may be any character.
if default / was used this was not required so:
sed '/--.*$/d' infile
or simply:
sed '/^--/d' infile
and more accurately:
sed '/^[[:blank:]]*--/d' infile

How do I correctly escape this search string for Perl pie

I use this classic perl one liner to replace strings in multiple files recursively
perl -pi -e 's/oldstring/newstring/g' `grep -irl oldstring *`
But this has failed me as I want to find the string:
'$user->primaryorganisation->id'
and replace with
$user->primaryorganisation->id
I can't seem to escape the string correctly for the line to run successfully.
Any help gratefully received!
Try this one. Lots of escapes. Go with TLPs suggestion and use a source file.
perl -pi -e "s/'\\\$user->primaryorganisation->id'/\\\$user->primaryorganisation->id/g" `grep -irl "'\$user->primaryorganisation->id'" *`
Explanation:
three backslashes: the first two tell the shell to produce a literal backslash; the thrid one escapes the $ for the shell; that makes \$ for Perl, which needs the backslash to escape the variable interpolation
double quotes " to put single quotes ' inside them
one backslash and a dollar \$ for grep so the shell passes on a literal dollar sign
When you want to represent a single quote in a perl but can't because the one-liner uses single quotes itself, you can use \047, the octal code for single quote. So, this should work:
s/\047(\$user->primaryorganisation->id)\047/$1/g
I recommend Minimal Perl by Maher for more-than-you-wanted-to-know about the art of one-lining perl.
To produce
...'...
you can generically use
'...'\''...'
As such,
s/'(\$user->primaryorganisation->id)'/$1/g
becomes
's/'\''(\$user->primaryorganisation->id)'\''/$1/g'
so
find -type f \
-exec perl -i -pe's/'\''(\$user->primaryorganisation->id)'\''/$1/g' {} +

Replace /**/ in Unix with an empty space

I wanted to replace /**/ across all files in a folder. I tried:
perl -pi -w -e 's/\/**\///g;' *.java
But got an error: Nested quantifiers in regex; marked by <-- HERE in m//** <-- HERE // at -e line 1.
Same thing with:
sed 's/\/**\///g;'
I need to basically replace /**/ with an empty space.
You need to escape the asterisks as well. To preserve readability, you may want to use other delimiters as well:
sed "s#/\*\*/##g"
You can specify * within []:
perl -pi -w -e 's#/[*]{2}/# #g;' *.java
Alternatively, you can make use of quotemeta operator:
perl -pi -w -e 's#\Q/**/\Q# #g;' *.java
which escapes all ASCII characters not matching /[A-Za-z_0-9]/.

Perl regex: remove everything (including line breaks) until a match is found

Apologies for the simple question. I don't clean text or use regex often.
I have a large number of text files in which I want to remove every line until my regex finds a match. There's usually about 15 lines of fluff before I find a match. I was hoping for a perl one-liner that would look like this:
perl -p -i -e "s/.*By.unanimous.vote//g" *.txt
But this doesn't work.
Thanks
Solution using the flip-flop operator:
perl -pi -e '$_="" unless /By.unanimous.vote/ .. 1' input-files
Shorter solution that also uses the x=!! pseudo operator:
per -pi -e '$_ x=!! (/By.unanimous.vote/ .. 1)' input-files
Have a try with:
If you want to get rid until the last By.unanimous.vote
perl -00 -pe "s/.*By.unanimous.vote//s" inputfile > outputfile
If you want to get rid until the first By.unanimous.vote
perl -00 -pe "s/.*?By.unanimous.vote//s" inputfile > outputfile
Try something like:
perl -pi -e "$a=1 if !$a && /By\.unanimous\.vote/i; s/.*//s if !$a" *.txt
Should remove the lines before the matched line. If you want to remove the matching line also you can do something like:
perl -pi -e "$a=1 if !$a && s/.*By\.unanimous\.vote.*//is; s/.*//s if !$a" *.txt
Shorter versions:
perl -pi -e "$a++if/By\.unanimous\.vote/i;$a||s/.*//s" *.txt
perl -pi -e "$a++if s/.*By\.unanimous\.vote.*//si;$a||s/.*//s" *.txt
You haven't said whether you want to keep the By.unanimous.vote part, but it sounds to me like you want:
s/[\s\S]*?(?=By\.unanimous\.vote)//
Note the missing g flag and the lazy *? quantifier, because you want to stop matching once you hit that string. This should preserve By.unanimous.vote and everything after it. The [\s\S] matches newlines. In Perl, you can also do this with:
s/.*?(?=By\.unanimous\.vote)//s
Solution using awk
awk '/.*By.unanimous.vote/{a=1} a==1{print}' input > output

How to replace a String which has escape sequence inside a File using Perl?

how to replace a String inside a File using perl ?
perl -pi -e 's/Arun/Brun/g' *
this worked fine for me
but when i tried to change class/students/a to class1/students1/B it throws error how to solve this problem ..i tried adding back slash (\) before every (/) but it didn't help
perl -pi -e 's/class/students/a/class1/students1/B/g' *
You are using / as regex delimiter.There are / even in your pattern and replacement. You need to somehow ensure that these / should not be treated as delimiter.
You have two options:
Escape the / in your pattern and replacement as:
perl -pi -e 's/class\/students\/a/class1\/students1\/B/g' *
Or use a different delimiter:
perl -pi -e 's#class/students/a#class1/students1/B#g' *
Method 2 is preferred as it keeps your regex short and clean.
Try perl -pi -e 's{class/students/a}{class1/students1/B}g' *
From perldoc perlop:
Any non-whitespace delimiter may
replace the slashes. Add space after
the s when using a character allowed
in identifiers. (...) If the
PATTERN is delimited by bracketing
quotes, the REPLACEMENT has its own
pair of quotes, which may or may not
be bracketing quotes, e.g.,
s(foo)(bar) or s/bar/.
Thank you perldoc perlop:
My problem is changin Oracle Home has solve by your command. one more trick I have done to replace with shell variable.
export ORACLE_HOME=/oracle/product/11.2.0.3/db_1
perl -pi -e 's{REPLACEWITHORACLEHOME}{'$ORACLE_HOME'}g' filename
more scripted way is :
for y in `ls`;do perl -pi -e 's{REPLACEWITHORACLEHOME}{'$ORACLE_HOME'}g' $y;done