How do I use perl like sed? - perl

I have a file that has some entries like
--ERROR--- Failed to execute the command with employee Name="shayam" Age="34"
--Successfully executed the command with employee Name="ram" Age="55"
--ERROR--- Failed to execute the command with employee Name="sam" Age="23"
--ERROR--- Failed to execute the command with employee Name="yam" Age="3"
I have to extract only the Name and Age of those for whom the command execution was failed.
in this case i need to extract shayam 34 sam 23 yam 3. I need to do this in perl. thanks a lot..

perl -p -e 's/../../g' file
Or to inline replace:
perl -pi -e 's/../../g' file

As a one-liner:
perl -lne '/^--ERROR---.*Name="(.*?)" Age="(.*?)"/ && print "$1 $2"' file

Your title makes it not clear. Anyway...
while(<>) {
next if !/^--ERROR/;
/Name="([^"]+)"\s+Age="([^"]+)"/;
print $1, " ", $2, "\n";
}
can do it reading from stdin; of course, you can change the reading loop to anything else and the print with something to populate an hash or whatever according to your needs.

As a one liner, try:
perl -ne 'print "$1 $2\n" if /^--ERROR/ && /Name="(.*?)"\s+Age="(.*?)"/;'
This is a lot like using sed, but with Perl syntax.

The immediate question of "how do I use perl like sed?" is best answered with s2p, the sed to perl converter. Given the command line, "sed $script", simply invoke "s2p $script" to generate a (typically unreadable) perl script that emulates sed for the given set of commands.

Refer to comments :
my #a = <>; # Reading entire file into an array
chomp #a; # Removing extra spaces
#a = grep {/ERROR/} #a; # Removing lines that do not contain ERROR
# mapping with sed-like regexp to keep only names and ages :
#a = map {s/^.*Name=\"([a-z]+)\" Age=\"([0-9]+)\".*$/$1 $2/; $_} #a;
print join " ",#a; # print of array content

Related

how should I use system command in perl

I want to replace a line with the $var.
runnew has
input= input/old/should/change;
replace= input/old/replace;
other = input/old/other;
replace_other= input/old/replace_other;
My output file should look like,
input= input/old/should/New;
replace= input/old/New_replace;
other = input/old/New_other;
replace_other= input/old/New_replace_other;
I want to replace "input =" by input = input/old/should/New;
I have used like,
if ($#ARGV != 0) {
die "\n********USAGE <cellname> <tech>********\n";
}
$newinput=$ARGV[0];
open(my $fh, "$runnew") or die "Could not open rerun.txt: $!";
while (<$fh>) {
system ( sed -i "/input=.*/c\input= $newinput" $runnew );
}
But there is error popping up "Scalar found where operator expected at run.pl" and its displaying sed line and asking " (Missing operator before $runnew?)."
When I used same sed on terminal its replacing the line .
Please can anyone point out where the error is ?
Yes Using Sed is simple but I have file with different lines and each line should be replaced .
Please let me knoe if you have better idea than this.
Thanks in advance.
system() takes a list of strings as its argument. You you need to put quotes around the command you pass it.
system ( "sed -i '/input=.*/c\input= $newinput' $runnew" );
But your code still looks very strange. You're running exactly the same sed command for every line in the input file. Is that what you meant to do?
It's not really clear what you're trying to do here. But I'm confident that the best approach would involve not using sed and using Perl to make your transformations.
Why do you want to call sed at all? Your requirement can be much easier handled in Perl directly:
add -i.bak to enable in-place replacement mode
use the first command line parameter as replacement string
remove it from the #ARGV array so it will not be interpreted as file
loop over all files on the command line
read line by line
apply substitution
print result
Perl automatically takes care of opening the files, writing to the correct file and renaming old files to .bak.
#!/usr/bin/perl -i.bak
use warnings;
use strict;
my($replacement) = shift(#ARGV);
while (<>) {
s/input=.*/input= $replacement/;
print;
}
exit 0;
Test run (taking an educated guess on your input data):
$ cat dummy1.txt.bak
input= test1
input= test2
$ cat dummy2.txt.bak
input= test3
input= test4
$ perl dummy.pl REPLACEMENT dummy1.txt dummy2.txt
$ cat dummy1.txt
input= REPLACEMENT
input= REPLACEMENT
$ cat dummy2.txt
input= REPLACEMENT
input= REPLACEMENT
or to use the contents of the file "rerun.txt":
$ perl dummy.pl REPLACEMENT $(cat rerun.txt)

How to escape ',' sign at command line?

When I do in perl script (source):
`perl -d:DebugHooks::DbInteract='s;e [\#DB::stack\,\#DB::goto_frames]' -e '1'`
The module gets two arguments:
s;e [\#DB::goto_frames\
\#DB::stack];
But I want to get only one:
s;e [\#DB::goto_frames,\#DB::stack];
How to escape ',' sign?
It's got to be your module splitting on the comma, not perl or the shell. Running the following under bash, I get only a single argument in #ARGV:
$ perl -w -E 'say join "\n", ("---", #ARGV, "---")' 's;e [\#DB::stack\,\#DB::goto_frames]'
---
s;e [\#DB::stack\,\#DB::goto_frames]
---
Edit:
I stand corrected. It is being split on commas by perl, presumably to allow passing multiple arguments to a module, as I proved to myself by creating a module at ./Devel/DbInteract.pm containing:
package Devel::DbInteract;
use strict;
use warnings;
use 5.010;
sub import {
say 'Received ' . scalar #_ . ' params:';
say join "\n", #_;
}
1;
and running the command:
$ PERL5LIB=. perl -d:DbInteract='s;e [\#DB::stack,\#DB::goto_frames]' -e ''
Received 3 params:
Devel::DbInteract
s;e [\#DB::stack
\#DB::goto_frames]
Judging by the source linked in the asker's answer, there does not appear to be any provision for escaping values or any other way to prevent this splitting. Your options, then, would be to either work around it by joining the params back together or submitting a patch to the perl source to add an allowance for escaping.
Perl does not care about escaping: https://github.com/Perl/perl5/blob/blead/perl.c#L3240-L3264
the -d flag just add next as zero line number into the script:
use Devel::DbInteract split(/,/,q{s;e [\#DB::stack\,\#DB::goto_frames]});
Some people advice me simple approach than patching perl. Just to use => in my case:
`perl -d:DebugHooks::DbInteract='s;e [\#DB::stack => \#DB::goto_frames]' -e '1'`

Awk not working inside a perl scipt?

I am trying to execute below awk command inside a perl script, but it is failing.
#!/usr/bin/perl
print `awk -F, '{print $NF}' f1.txt > f2.txt`
This is the error:
syntax error at ./MO.pl line 3, near "print"
Execution of ./MO.pl aborted due to compilation errors.
Can anyone please help what I am doing wrong here?
This is a Perl error and has nothing to do with your awk script itself. The error is usually seen when the previous statement doesn't have a semicolon at the end.
Here's a very simple program (which should include use strict; and use warnings;, but I wanted to emulate what you have).
#! /usr/bin/env perl
#
print "Hello, World\n" # Line 4
print "Hello, Back\n"; # Line 5
And the error message is:
syntax error at test.pl line 5, near "print"
Execution of test.pl aborted due to compilation errors.
Note the error is near the print in Line #5, but the error is actually at the end of Line #4 where I'm missing a semicolon.
Running your exact program works on my system (although doesn't quite produce the results you want). I am assuming this isn't your exact program, but instead a simplification of your program. Is there a statement before that print?
Several other things:
You're redirecting your awk output, so there's nothing to print.
Use strict and warnings.
Better to use qx(....) than backticks (grave accent). It's more readable and allows you to do quoted executable in quoted executable.
Watch for Perlisms in your code. The $NF is interpreted by Perl, and without the use strict;, doesn't give you an error. Instead, the print in your Awk statement is a null print which prints the entire line.
Why do you use print if nothing is printing out? You're better off in this position to use system which allows you to put single quotes around your entire statement:
system q(awk -F, '{print $NF}' f1.txt > f2.txt);
This way, $NF doesn't have to be quoted.
Why are you doing Awk in a Perl program? Perl will do anything Awk will do and do it better:
Here's a version of your program using plain ol' Perl:
#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
while ( my $line = <> ) {
my #array = split /\s*,\s*/, $line;
print $array[-1];
}
To run this program:
$ test.pl f1.txt > f2.txt
Yes, it's longer, but half of the program is taken up by pragmas that all Perl programs should use.
I'm sure people smarter than me can turn this into a Perl one-liner.
Since you're redirecting the awk output, there's nothing for perl to print. You might as well use system and the quoting operator q():
system q(awk -F, '{print $NF}' f1.txt > f2.txt)
Or, of course, do it in perl, which saves you from having to spawn a shell and then spawn awk:
open my $in, '<', 'f1.txt';
open my $out, '>', 'f1.txt';
while (<$in>) {
print $out (split " ")[-1], "\n";
}
close $in;
close $out;
If there are more lines in the script, you need a semi-colon at the end of the print statement.

SED command not working in perl script

When i am using "sed" in command line it is working but not when included in perl script.
An example is sed 's/\s+//g' aaa > bbb
but say when i am trying to call the same command through perl script
$gf = `sed 's/\s\+//g' aaa > bbb` ;
the output file remains same as the input file!!!! Please suggest.
In Perl, backticks have the same escape and interpolation rules as double quoted strings: A backslash forming an unknown escape code forgets the backslash, e.g. "\." eq ".".
Therefore, the Perl code
print `echo \"1\"`;
print `echo \\"1\\"`;
outputs
1
"1"
If you want to embed that sed command into Perl, you have to escape the backslashes so that they even reach the shell:
$gf = `sed 's/\\s\\+//g' aaa > bbb`;
Actually, you won't get any output into $gf as you redirect the output to a file. We could just do
use autodie;
system "sed 's/\\s\\+//g' aaa > bbb";
or with single quotes:
use autodie;
system q{ sed 's/\s\+//g' aaa > bbb };
which keeps the backslashes.
Still, this is quite unneccessary as Perl could apply the substitution itself.
use autodie; # automatic error handling
open my $out, ">", "bbb";
open my $in, "<", "aaa";
while (<$in>) {
s/\s\+//g; # remove all spaces followed by a plus
print {$out} $_;
}
In these weird situations, I ensure that I'm running the right command. I'll construct it, store it, and output the command so I can see exactly what I created:
my $command = '....';
print "Command is [$command]\n";
my $output = `$command`;
If you're running sed from Perl, you might be doing it wrong since Perl can already do all that.
have you got
use strict;
use warnings;
at the top of your file?
you could need backticks to execute the command
$gf = `sed 's/\s\+//g' aaa > bbb`;

Perl oneliner match repeating itself

I'm trying to read a specific section of a line out of a file with Perl.
The file in question is of the following syntax.
# Sets $USER1$
$USER1$=/usr/....
# Sets $USER2$
#$USER2$=/usr/...
My oneliner is simple,
perl -ne 'm/^\$USER1\$\s*=\s*(\S*?)\s*$/m; print "$1";' /my/file
For some reason I'm getting the extraction for $1 repeated several times over, apparently once for every line in the file after my match occurs. What am I missing here?
You are executing print for every line of the file because print gets called for every line, whether the regex matches or not. Replace the first ; with an &&.
From perlre:
NOTE: Failed matches in Perl do not reset the match variables, which makes it easier to write code that tests for a series of more specific cases and remembers the best match.
Try this instead:
perl -ne 'print "$1" if m/^\$USER1\$\s*=\s*(\S*?)\s*$/m;' /my/file
$ cat test.txt
# Sets $USER1$
$USER1$=/usr/....
# Sets $USER2$
#$USER2$=/usr/...
$ perl -nle 'print if /^\$USER1/;' test.txt
$USER1$=/usr/....
Try this
perl -ne '/^.*1?=([\w\W].*)$/;print "$1";' file