unmatched parenthesis in regex - Linux - regex

I want to replace (whole string)
$(TOPDIR)/$(OSSCHEMASDIRNAME)
with
/udir/makesh/$(OSSCHEMASDIRNAME)
in a makefile
I tried with
perl -pi.bak -e "s/\$\(TOPDIR\)\/\$\(OSSCHEMASDIRNAME\)/\/udir\/makesh\/\$\(OSSCHEMASDIRNAME\)/g " makefile
but i am getting unmatched parentheses error

You have to "double" escape the dollar sign. Like this:
echo "\$(TOPDIR)/\$(OSSCHEMASDIRNAME)" | perl -p -e "s/\\$\(TOPDIR\)\/\\$\(OSSCHEMASDIRNAME\)/\/udir\/makesh\/\\$\(OSSCHEMASDIRNAME\)/g"

First off, you don't need to use / for regular expressions. They're just canonical. You can use pretty much anything. Thus your code can become (simplify away some \):
perl -pi.bak -e "s|\$\(TOPDIR\)/\$\(OSSCHEMASDIRNAME\)|/udir/makesh/\$\(OSSCHEMASDIRNAME\)|g " makefile
Now to actually address your issue, because you're using " instead of ', the shell attempts to figure out what $\ means which is then replaced with (presumably) nothing. So what you really want is:
perl -p -i.bak -e 's|\$\(TOPDIR\)/\$\(OSSCHEMASDIRNAME\)|/udir/makesh/\$\(OSSCHEMASDIRNAME\)|g' makefile

When in doubt about escaping, you can simply use quotemeta or \Q ... \E.
perl -pe 's#\Q$(TOPDIR)\E(?=/\Q$(OSSCHEMASDIRNAME)\E)#/udir/makesh#;'
Note the use of a look-ahead assertion to save us the trouble of repeating the trailing part in the substitution.
A quotemeta solution would be something like:
perl -pe 'BEGIN { $dir = quotemeta(q#$(TOPDIR)/$(OSSCHEMASDIRNAME)#); }
s#$dir#/udir/makesh/$(OSSCHEMASDIRNAME)#;'
Of course, you don't need to use an actual one-liner. When the shell quoting is causing troubles, the simplest option of them all is to write a small source file for your script:
s#\Q$(TOPDIR)\E(?=/\Q$(OSSCHEMASDIRNAME)\E)#/udir/makesh#;
And run with:
perl -p source.pl inputfile

Related

perl pie escape the delimeter

I'm running this perl cmd on Mac OS to delete the whole line.
perl -i -pe's/<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="#string/fb_app_id"/>/ /g' AndroidManifest.xml
The result is
fb_app_id"#string/fb_app_id"/>
I'm unable to escape the / in "#string/fb_app_id tried different variations #string//fb_app_id and #string\/fb_app_id but none worked.
If you had been using warnings, you would have been notified that #strings is being considered a variable and interpolated in the regex. You should try to escape the #. And also the slashes.
perl -i -pe's/<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="\#string\/fb_app_id"\/>\/ /g' AndroidManifest.xml
Running a Perl command without warnings is a bad idea, even if you are a Perl expert.
Notable things:
You should not parse XML with a regex. Related infamous answer.
You do not need to use a substitution, you can try a looser match using m// with the -n switch, and then avoid printing matching lines. E.g.
perl -i -nwe'print unless m|android:value="\#string/fb_app_id"|'
Can you try using delimiter [] ?
perl -i -pe's[<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="\#string/fb_app_id"/>][ ]g' AndroidManifest.xml
Notwithstanding the hazzards of using regex on XML, here is how you would normally do a task like this.
Use curly braces as the delimiters. s{}{}
Manually escape these characters. $ # \ --> \$ \# \\
If it's a fixed string, wrap it with \Q\E. Otherwise periods and other regex meta characters will cause unintended effects.
perl -i.bak -pe 's{\Q<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="\#string/fb_app_id"/>\E}{ }g' AndroidManifest.xml
To merely comment out the block:
perl -i.bak -pe 's{(\Q<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="\#string/fb_app_id"/>\E)}{<!-- $1 -->}g' AndroidManifest.xml
HTH

Perl regex single quote

Can someome help me to run the command below. I also tried to escape the single quotes but no luck.
perl -pi.bak -e 's/Object\.prototype\.myString='q'//' myfile.html
The problem is not with Perl, but with your shell. To see what's happening, try this:
$ echo 's/Object\.prototype\.myString='q'//'
s/Object\.prototype\.myString=q//
To make it work, you can replace each single quote with '\'', like this:
$ echo 's/Object\.prototype\.myString='\''q'\''//'
s/Object\.prototype\.myString='q'//
or you can save a few characters by writing just:
$ echo 's/Object\.prototype\.myString='\'q\''//'
s/Object\.prototype\.myString='q'//
or even just:
$ echo 's/Object\.prototype\.myString='\'q\'//
s/Object\.prototype\.myString='q'//
or even:
$ echo s/Object\\.prototype\\.myString=\'q\'//
s/Object\.prototype\.myString='q'//
Double quotes, as suggested by mu is too short, will work here too, but can cause unwanted surprises in other situations, since many characters commonly found in Perl code, like $, ! and \, have special meaning to the shell even inside double quotes.
Of course, an alternative solution is to replace the single quotes in your regexp with the octal or hex codes \047 or \x27 instead:
$ perl -pi.bak -e 's/Object\.prototype\.myString=\x27q\x27//' myfile.html
Double quotes should work:
perl -pi.bak -e "s/Object\.prototype\.myString='q'//" myfile.html
You may or may not want a g modifier on that regex. And you'll probably want to do a diff after to make sure you didn't mangle the HTML.

escape reserved chars from a regexp(sed) parameter

I want to write a script that modifies a variable in a .properties file. The user enters the new value which is in turn written into the file.
read -p "Input Variable?" newVar
sed -r 's/^\s*myvar=.*/myvar=${newVar}/' ./config.properties
Unfortunately problems arise when the user inputs special characters. In my use case it is very likely that a "/" character is typed. So my guess is that I have to parse ${newVar} for all slashes and escape them? But how? Is there a better way?
have a look at bash printf
%q quote the argument in a way that can be reused as shell input
Example:
$ printf "%q" "input with special characters // \\ / \ $ # #"
input\ with\ special\ characters\ //\ \\\ /\ \\\ \$\ #\ #
Avoiding shell quoting is a good general principle.
#! /usr/bin/env perl
use strict;
use warnings;
die "Usage: $0 properties-file ..\n" unless #ARGV;
print "New value for myvar?\n";
chomp(my $new = <STDIN>);
$^I = ".bak";
while (<>) {
s/^(\s*myvar\s*=\s*).*$/$1$new/;
print;
}
Substitution with s/// as above will be familiar to sed users.
The code above uses Perl's in-place editing facility (enabled most commonly with the -i switch but above with the special $^I variable) to modify files named on the command line and create backups with the .bak extension.
Example usage:
$ cat foo.properties
theirvar=123
myvar=FIXME
$ ./prog foo.properties
New value for myvar?
foo\bar
$ cat foo.properties
theirvar=123
myvar=foo\bar
$ cat foo.properties.bak
theirvar=123
myvar=FIXME
Edit: oops, we are only qoting the value, not the regex. So this is what you need
You are better off using perl instead of sed for this if it is available.
read -p "Input Variable?" newVar
perl -i -p -e 'BEGIN{$val=shift;}' \
-e 's/^\s*myvar=.*/myvar=$val/' \
"$newVar" ./config.properties
Edit2: Sorry, still does not handle \ characters in newVar. Guess one of the other solutions is better. As stated before, dealing with shell escaping is your issue.
You are better off using a tool that understands variables -- Perl, maybe AWK -- trying to quote a random string so that you avoid all unintended interactions with sed command parsing is asking for trouble.
Also, you won't get your variable interpolated when using single quotes, and even with -r, sed does not grok Perl regex syntax -- -r only gets you to the egrep version of regexes, so \s doesn't do what you want.
Anyway, ignoring my own advice, here's how we'd do it in the old days before we had those better tools:
read -p "Input Variable?" newVar
sed "/^ *myvar=/c\\
myvar=`echo \"$newVar\" | sed 's/\\\\/\\\\\\\\/'`" ./config.properties
If you don't think your users will figure out how to input literal backslashes at your prompt, you can simplify this to:
read -p "Input Variable?" newVar
sed "/^ *myvar=/c\\
myvar=$newVar" ./config.properties

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

Return only matches from substitution in Perl 5.8.8 (was: Perl "p" regex modifier equivalent)

I've got a script (source) to parse svn info to create a suitable string for Bash's $PS1. Unfortunately this doesn't work on one system I'm using which is running Perl 5.8.8 - It outputs all lines instead of only the matches. What would be the Perl 5.8.8 equivalent to the following?
__svn_ps1()
{
local result=$(
svn info 2>/dev/null | \
perl -pe 's;^URL: .*?/((trunk)|(branches|tags)/([^/]*)).*;\2\4 ;p')
if [ -n "$result" ]
then
printf "${1:- (%s)}" $result
fi
}
The output from Perl 5.10 contains only a space, parenthesis, one of branch name, tag name or trunk, and the end parenthesis. The output from Perl 5.8.8 (without the final p) contains this plus a parenthesized version of each space-separated part of the svn info output.
A possible workaround involves a simple grep '^URL: ' between the svn and perl commands, but I was hoping to avoid that since this will be executed for each Bash prompt.
If you only want output from a line that matches, don't use the -p command-line switch. It prints the value of $_ at the end of each loop. You might want something with the -n command-line switch:
perl -ne 'print if s/.../.../'
I'd do it in the same way for Perl v5.8 and v5.10. I'm not sure what you think the /p modifier is doing since you don't use the $`, $&, or $' variables or their per-match equivalents.
You can read about the command-line switches in perlrun.
As of perl 5.10, the /p switch tells perl to put matched content into ${^PREMATCH}, ${^MATCH} and ${^POSTMATCH}.
And the one-liner you've posted never uses those vars, so omit the /p.
UPDATE: Trying to keep up with the initial question...
perl -ne 's/search/replace/ and print'
Will only print lines for which the replacement was made. Note -n versus -p switch. Also, I've tried the -p /p combo on my 5.10 and it happily prints unaltered non-matching lines too. Maybe I missed something...