How to suppress piped input sent to perl on command line? - perl

At my comand prompt, I ran a grep and got the following result.
$ grep -r "javascript node"
restexample/NewsSearchService/V1/madonna_html.html:<!-- start empty javascript node for popup app fix -->
restexample/NewsSearchService/V1/madonna_html.html:<!-- end empty javascript node for popup app fix -->
Now, suppose I want to remove the "restexample" part. I can do that by using
print substr($_,13)
However, how when I pipe to perl, this is what I get -
grep -r "javascript node" | perl -pe ' print substr($_,11) '
/NewsSearchService/V1/madonna_html.html:<!-- start empty javascript node for popup app fix -->
restexample/NewsSearchService/V1/madonna_html.html:<!-- start empty javascript node for popup app fix -->
/NewsSearchService/V1/madonna_html.html:<!-- end empty javascript node for popup app fix -->
restexample/NewsSearchService/V1/madonna_html.html:<!-- end empty javascript node for popup app fix -->
As you can see, the piped input simply got echoed. How to prevent this?

Try
grep -r "javascript node" | perl -lpe '$_ = substr($_,11)'
or
grep -r "javascript node" | perl -lne 'print substr($_,11)'
Explanation: -p switch automatically prints current line ($_) while -n switch doesn't.
perl -MO=Deparse -lpe '$_ = substr($_,11)'
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
$_ = substr($_, 11);
}
continue {
die "-p destination: $!\n" unless print $_; # <<< automatic print
}

Related

In perl, how can I tee output to stdout and a file for system() return vals?

I'm successfully using
IO::Tee
to tee print messages to both stdout and a log file. I'd like to also be able to capture and tee stdout and stderr from a system() commands to the same file.
I tried various combinations of redirection and/or pipes with no luck
Simple script witha few commented out failed attempts...
#!/usr/bin/env perl
use IO:Tee;
my $teelog = "tee.log"
open my $tee, ">", $teelog or die "open tee failed.\n";
my $tee = new IO:Tee(\*STDOUT, $tee);
print $tee "First line in log\n";
# This command should work. I want the date to go to screen and tee.log
#system("date | tee -a ${teelog}"); <- nothing goes to tee.log
#system("date >& ${teelog}") <- clobbers tee.log
#system("date >& $tee") <- generates syntax errors
# This command should fail. I want error msg to go to screen and tee.log
#system("jibberish | tee -a ${teelog}"); <- nothing goes to tee.log
#system("jibberish >& ${teelog}") <- clobbers tee.log
system("kibberish >& $tee") <- generates syntax errors
print $tee "Last line in log\n";
exit;
Perl is buffering output to the $tee filehandle and the cursor position of $tee is not adjusted after you make a system call that writes to the same file, so it is likely that Perl is overwriting whatever output the system call writes to the file.
A sequence of calls like this is safer. You may be able to be more concise with more judicious seeking and flushing.
use IO:Tee;
my $teelog = "tee.log"
open my $tee, ">", $teelog or die "open tee failed.\n";
$tee = new IO:Tee(\*STDOUT, $tee);
print $tee "First line in log\n";
close $tee;
system("date | tee -a ${teelog}");
system("jibberish 2>&1 | tee -a ${teelog}");
open $tee, ">>", $teelog or die "open tee failed.\n"; # append mode
$tee = new IO:Tee(\*STDOUT, $tee);
print $tee "Last line in log\n";
exit;
Thank You mob for this explanation and suggestion.
I also found another way using capture_exec in IO:CaptureOutput. I tested it with a sub that looks like this....
sub tee_sys_cmd {
(my $cmd) = #_;
print $tee "Executing... \"$cmd\"\n";
my ($stdout, $stderr, $success, $exit_code) = capture_exec($cmd);
print $tee "$stdout\n$stderr\n";
return;
}
This seems to work too.
You're discarding the status returned by system, so I would use backticks instead:
print $tee `date`

Perl eating line one on -n commandline option flag

I've started playing around with perl and I'm trying to figure out what is wrong with telling perl to use a loop if I also provide a loop?
It looks like perl is getting confused with the same open file descriptors but what I don't get is why does it eat the first line?
perl -ne 'while (<>) { print $_; }'
Of course in this simple example, I can simply perl -ne '{print $_}' to arrive at the same functional logic.
But what I want to know is what is going wrong with the double loop that the first line disappears if yet another while (<>) { } gets wrapped?
$ perl -ne '{print $_}' hello
hello
hello
world
world
^C
$ perl -ne 'while (<>) { print $_; }'
hello
world
world
^C
Update: According to the answers what seems to be happening is that Perl is waiting on the first loop for STDIN input. Upon receiving input on STDIN, the input is assigned to the internal buffer $_ and the logic proceeds to the second loop where again it waits for new STDIN input. Upon receiving new STDIN input, it clobbers the STDIN buffer $_ with the new STDIN input and begins printing.
You can itself check the code generated by one-liner using O=Deparse.
First:
$ perl -MO=Deparse -ne 'print $_;' file
LINE: while (defined($_ = <ARGV>)) {
print $_;
}
-e syntax OK
Second:
$ perl -MO=Deparse -ne 'while (<>) { print $_; }' file
LINE: while (defined($_ = <ARGV>)) {
while (defined($_ = <ARGV>)) {
print $_;
}
}
-e syntax OK
Now, It is easy to know what is wrong with second case. Outer while eats the first line of file and it is lost.
The -n flag wraps your code inside a while (<>) { ... } construct.
So in your second example, the code that is actually executed is
while (<>) # reads a line from STDIN, places it in $_
{
# you don't do anything with the contents of $_ here
while (<>) # reads a line from STDIN, places it in $_, overwriting the previous value
{
print $_; # prints the contents of $_
}
}
Which means the line that was read by the first <> is just lost.

running awk command in perl

I have a tab delimited file(dummy) that looks like this :
a b
a b
a c
a c
a b
I am trying to write an awk command inside the perl script in which the file.txt is being made.
The awk command :
$n=system(" awk -F"\t" '{if($1=="a" && $2=="b") print $1,$2}' file.txt|wc -l ")
Error :
comparison operator :error in '==' , ',' between $1 and $2 in print }'
The awk script is running fine on command line but giving error while running inside the script.
I don't see any syntax error in the awk command.
Aside from the fact that what are you trying to achieve by executing awk from within perl (since it could be accomplished using the latter itself), you could use the q operator:
$cmd = q(awk -F"\t" '{if($1=="a" && $2=="b") print $1,$2}' file.txt | wc -l);
$n = system($cmd);
Note that using double-quotes would interpolate variables and you'd need to escape those.
You can get the number of a\tbs from Perl itself without calling an external command:
#!/usr/bin/perl
use warnings;
use strict;
open my $FH, '<', 'file.txt' or die $!;
my $n = 0;
"a\tb\n" eq $_ and $n++ while <$FH>;
print "$n\n";

How to add blank line after every grep result using Perl?

How to add a blank line after every grep result?
For example, grep -o "xyz" may give something like -
file1:xyz
file2:xyz
file2:xyz2
file3:xyz
I want the output to be like this -
file1:xyz
file2:xyz
file2:xyz2
file3:xyz
I would like to do something like
grep "xyz" | perl (code to add a new line after every grep result)
This is the direct answer to your question:
grep 'xyz' | perl -pe 's/$/\n/'
But this is better:
perl -ne 'print "$_\n" if /xyz/'
EDIT
Ok, after your edit, you want (almost) this:
grep 'xyz' * | perl -pe 'print "\n" if /^([^:]+):/ && ! $seen{$1}++'
If you don’t like the blank line at the beginning, make it:
grep 'xyz' * | perl -pe 'print "\n" if /^([^:]+):/ && ! $seen{$1}++ && $. > 1'
NOTE: This won’t work right on filenames with colons in them. :)½
If you want to use perl, you could do something like
grep "xyz" | perl -p -e 's/(.*)/\1\n/g'
If you want to use sed (where I seem to have gotten better results), you could do something like
grep "xyz" | sed 's/.*/\0\n/g'
This prints a newline after every single line of grep output:
grep "xyz" | perl -pe 'print "\n"'
This prints a newline in between results from different files. (Answering the question as I read it.)
grep 'xyx' * | perl -pe '/(.*?):/; if ($f ne $1) {print "\n"; $f=$1}'
Use a state machine to determine when to print a blank line:
#!/usr/bin/env perl
use strict;
use warnings;
# state variable to determine when to print a blank line
my $prev_file = '';
# change DATA to the appropriate input file handle
while( my $line = <DATA> ){
# did the state change?
if( my ( $file ) = $line =~ m{ \A ([^:]*) \: .*? xyz }msx ){
# blank lines between states
print "\n" if $file ne $prev_file && length $prev_file;
# set the new state
$prev_file = $file;
}
# print every line
print $line;
}
__DATA__
file1:xyz
file2:xyz
file2:xyz2
file3:xyz

How to write a Perl script to convert file to all upper case?

How can I write a Perl script to convert a text file to all upper case letters?
perl -ne "print uc" < input.txt
The -n wraps your command line script (which is supplied by -e) in a while loop. A uc returns the ALL-UPPERCASE version of the default variable $_, and what print does, well, you know it yourself. ;-)
The -p is just like -n, but it does a print in addition. Again, acting on the default variable $_.
To store that in a script file:
#!perl -n
print uc;
Call it like this:
perl uc.pl < in.txt > out.txt
$ perl -pe '$_= uc($_)' input.txt > output.txt
perl -pe '$_ = uc($_)' input.txt > output.txt
But then you don't even need Perl if you're using Linux (or *nix). Some other ways are:
awk:
awk '{ print toupper($0) }' input.txt >output.txt
tr:
tr '[:lower:]' '[:upper:]' < input.txt > output.txt
$ perl -Tpe " $_ = uc; " --
$ perl -MO=Deparse -Tpe " $_ = uc; " -- a s d f
LINE: while (defined($_ = <ARGV>)) {
$_ = uc $_;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
$ cat myprogram.pl
#!/usr/bin/perl -T --
LINE: while (defined($_ = <ARGV>)) {
$_ = uc $_;
}
continue {
die "-p destination: $!\n" unless print $_;
}