I am using Perl to search and replace multiple regular expressions:
When I execute the following command, I get an error:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g' -pe 's/(\W)##/\1/g'
syntax error at -e line 2, near "s/(\W)##/\1/g"
Execution of -e aborted due to compilation errors.
xargs: perl: exited with status 255; aborting
Having multiple -e is valid in Perl, then why is this not working? Is there a solution to this?
Several -e's are allowed.
You are missing the ';'
find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g;' -pe 's/(\W)##/\1/g;'
Perl statements has to end with ;.
Final statement in a block doesn't need a terminating semicolon.
So a single -e without ; will work, but you will have to add ; when you have multiple -e statements.
Having multiple -e values are valid, but is it useful? The values from the multiple -e are merely combined into one program, and it's up to you to ensure that together they make a syntactically correct program. The B::Deparse program can show you what perl thinks the program is:
$ perl -MO=Deparse -e 'print' -e 'q(Hello' -e ')'
print "Hello\n";
-e syntax OK
A curious thing to note is that a newline snuck in there. Think about how it got there to see what else perl is doing to combine multiple -e values.
In your program, you are substituting on the current line, then taking the modified line and substituting again. That's better written as:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g; s/(\W)##/\1/g'
Now, if you are building up this command line by adding more and more -e through some automated process and you don't know ahead of time what you get, maybe those -e make sense. However, you might consider that you can do the same thing to build up the string you give to -e. I don't know what might be better because you didn't explain why you are doing it that way.
But, I suspect that in some cases, people are actually thinking about having only one substitution work. They want to try one and if its pattern doesn't work, try a different one until one succeeds. In that case you don't want to separate the substitutions by semicolons. Use the short-circuiting || instead. The s/// returns the number of substitutions it made and || will stop (short circuit) when it finds a true value:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g || s/(\W)##/\1/g'
And note, you only need one -p. It only does its job once. Here's the program with multiple -p deparsed:
$ perl -MO=Deparse -i -pe 's/##(\W)/\1/g;' -pe 's/(\W)##/\1/g;'
BEGIN { $^I = ""; }
LINE: while (defined($_ = readline ARGV)) {
s/##(\W)/$1/g;
s/(\W)##/$1/g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
It's the same thing as having only one -p:
$ perl -MO=Deparse -pi -e 's/##(\W)/\1/g;' -e 's/(\W)##/\1/g;'
BEGIN { $^I = ""; }
LINE: while (defined($_ = readline ARGV)) {
s/##(\W)/$1/g;
s/(\W)##/$1/g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
Thanks so much! You helped me reduce my ascii / decimal / 8-bit binary table printer enough to fit in a tweet:
for i in {32..126};do printf "'\x$(printf %x $i)'(%3i) = " $i; printf '%03o\n' $i | perl \
-pe 's#0#000#g;' -pe 's#1#001#g;' -pe 's#2#010#g;' -pe 's#3#011#g;' \
-pe 's#4#100#g;' -pe 's#5#101#g;' -pe 's#6#110#g;' -pe 's#7#111#g' ; done | \
perl -pe 's#= 0#= #'
Related
What is the difference between the perl -n and perl -p options?
What is a simple example to demonstrate the difference?
How do you decide which one to use?
How do you decide which one to use?
You use -p if you want to automatically print the contents of $_ at the end of each iteration of the implied while loop. You use -n if you don't want to print $_ automatically.
An example of -p. Adding line numbers to a file:
$ perl -pe '$_ = "$.: $_"' your_file.txt
An example of -n. A basic grep replacement.
$ perl -ne 'print if /some search text/' your_file.txt
-p is short for -np, and it causes $_ to be printed for each pass of the loop created by -n.
perl -ne'...'
executes the following program:
LINE: while (<>) {
...
}
while
perl -pe'...'
executes the following program:
LINE: while (<>) {
...
}
continue {
die "-p destination: $!\n" unless print $_;
}
See perlrun for documentation about perl's command-line options.
What is the difference between the perl -n and perl -p options?
-p causes each line to be printed; equivalent to:
while (<>) { ... } continue { print }
-n does not automatically print each line; equivalent to:
while(<>) {...}
What is a simple example to demonstrate the difference?
e.g., replace foo with FOO:
$ echo 'foo bar' | perl -pe 's/foo/FOO/'
FOO bar
$ echo 'foo bar' | perl -ne 's/foo/FOO/'
$
How do you decide which one to use?
One example where -n is useful is when you don't want every line printed, and there is a conditional print in the code, e.g., only show lines containing foo:
$ echo -e 'foo\nbar\nanother foo' | perl -ne 'print if /foo/;'
foo
another foo
$
The command-line options are documented in perlrun documentation
perl -n is equivalent to while(<>){...}
perl -p is equivalent to while(<>){...;print;}
I am trying to replace a windows line ending character \r\n with linux style line ending character \n in a text file using a perl script. What I have written in the script is as the following:
system("perl -pi -e s/\r\n/\n/g input.txt");
And the resulting warning is:
Substitution pattern not terminated at -e line 1
I'd glad if I learn where this command is wrong.
Edit: When I used:
system("perl -pi -e 's/\r\n/\n/g' words.txt");
I got this warning:
Can't find string terminator "'" anywhere before EOF at -e line 1
Thanks in advance.
Separate the args for your system call. That will help you avoid needing to use the proper quoting and escaping:
system('perl', '-i', '-pe', 's/\r\n/\n/g', 'input.txt');
However, I'd recommend just doing this with $INPLACE_EDIT:
my $file = 'input.txt';
local #ARGV = $file;
local $^I = '';
while (<>) {
s/\R/\n/;
print;
}
perl -p -i -e 's/\r\n/\n/g' input.txt
You are missing '' in your regex. hence the error Substitution pattern not terminated at -e line 1
you are running the script on Windows, so you have to change the simple quotes and double quotes, and define the extension of the -i flag.
system('perl -i.bak -p -e "s/\r\n/\n/g" words.txt');
This question already has answers here:
How can I process options using Perl in -n or -p mode?
(2 answers)
Closed last year.
I often need to run some Perl one-liners for fast data manipulations, like
some_command | perl -lne 'print if /abc/'
Reading from a pipe, I don't need a loop around the command arg filenames. How can I achieve the next?
some_command | perl -lne 'print if /$ARGV[0]/' abc
This gives the error:
Can't open abc: No such file or directory.
I understand that the '-n' does the
while(<>) {.... }
around my program, and the <> takes args as filenames, but doing the next every time is a bit impractical
#/bin/sh
while read line
do
some_command | perl -lne 'BEGIN{$val=shift #ARGV} print if /$val/' "$line"
done
Is there some better way to get "inside" the Perl ONE-LINER command line arguments without getting them interpreted as filenames?
Some solutions:
perl -e'while (<STDIN>) { print if /$ARGV[0]/ }' pat
perl -e'$p = shift; while (<>) { print if /$p/ }' pat
perl -e'$p = shift; print grep /$p/, <>' pat
perl -ne'BEGIN { $p = shift } print if /$p/' pat
perl -sne'print if /$p/' -- -p=pat
PAT=pat perl -ne'print if /$ENV{PAT}/'
Of course, it might make more sense to create a pattern that's an ORing or all patterns rather than executing the same command for each pattern.
Also reasonably short:
... | expr=abc perl -lne 'print if /$ENV{expr}/'
Works in bash shell but maybe not other shells.
It depends on what you think will be in the lines you read, but you could play with:
#/bin/sh
while read line
do
some_command | perl -lne "print if /$line/"
done
Clearly, if $line might contain slashes, this is not going to fly. Then, AFAIK, you're stuck with the BEGIN block formulation.
I'm trying to extract all ip addresses from a file. So far, I'm just using
cat foo.txt | perl -pe 's/.*?((\d{1,3}\.){3}\d{1,3}).*/\1/'
but this also prints lines that don't contain a match. I can fix this by piping through grep, but this seems like it ought to be unnecessary, and could lead to errors if the regexes don't match up perfectly.
Is there a simpler way to accomplish this?
Try this:
cat foo.txt | perl -ne 'print if s/.*?((\d{1,3}\.){3}\d{1,3}).*/\1/'
or:
<foo.txt perl -ne 'print if s/.*?((\d{1,3}\.){3}\d{1,3}).*/\1/'
It's the shortest alternative I can think of while still using Perl.
However this way might be more correct:
<foo.txt perl -ne 'if (/((\d{1,3}\.){3}\d{1,3})/) { print $1 . "\n" }'
If you've got grep, then just call grep directly:
grep -Po "(\d{1,3}\.){3}\d{1,3}" foo.txt
You've already got a suitable answer of using grep to extract the IP addresses, but just to explain why you were seeing non-matches being printed:
perldoc perlrun will tell you about all the options you can pass Perl on the command line.
Quoting from it:
-p causes Perl to assume the following loop around your program, which makes it
iterate over filename arguments somewhat like sed:
LINE:
while (<>) {
... # your program goes here
} continue {
print or die "-p destination: $!\n";
}
You could have used the -n switch instead, which does similar, but does not automatically print, for example:
cat foo.txt | perl -ne '/((?:\d{1,3}\.){3}\d{1,3})/ and print $1'
Also, there's no need to use cat; Perl will open and read the filenames you give it, so you could say e.g.:
perl -ne '/((?:\d{1,3}\.){3}\d{1,3})/ and print $1' foo.txt
ruby -0777 -ne 'puts $_.scan(/((?:\d{1,3}\.){3}\d{1,3})/)' file
I have a file that contains a lot of these
"/watch?v=VhsnHIUMQGM"
and I would like to output the letter code using a perl one-liner. So I try
perl -nle 'm/\"\/watch\?v=(.*?)\"/g' filename.txt
but it doesn't print anything.
What am I doing wrong?
The -n option processes each line but doesn't print anything out. So you need to add an explicit print if you successfully match.
perl -ne 'while ( m/\"\/watch\?v=(.+?)\"/g ) { print "$1\n" }' filename.txt
Another approach, if you're sure every line will match, is to use the -p option which prints out the value of $_ after processing, e.g.:
perl -pe 's/\"\/watch\?v=(.+?)\"/$1//' filename.txt
Your regex is fine. You're getting no output because the -n option won't print anything. It simply wraps a while (<>) { ... } loop around your program (run perl --help for brief explanations of the Perl options).
The following uses your regex, but add some printing. In list context, regexes with the /g option return all captures. Effectively, we print each capture.
perl -nle 'print for m/\"\/watch\?v=(.*?)\"/g' data.dat
You can split the string on "=" instead of matching:
perl -paF= -e '$_= #F[1]' filename.txt