PERL: repeated lines - perl

I'm writing a perl code that print a massage/send a mail if there is a repeated line found in a file.
My code so far:
#!/usr/bin/perl
use strict;
my %prv_line;
open(FILE, "somefile") || die "$!";
while(<FILE>){
if($prv_line{$_}){
$prv_line{$_}++;
}
#my problem: print I saw this line X times
}
close FILE
My problem: How do generate a static msg with output: print "I saw this line X times" without printing the script output
Thanks

probably, here's what you want:
#!/usr/bin/perl
use strict;
use warnings;
my %lines;
while(<DATA>) {
chomp;
$lines{$_}++;
}
while (my($key, $value) = each %lines) {
print "I saw the line '$key' $value times\n";
}
__DATA__
abc
def
def
def
abc
blabla
avaddv
bla
abc
Of course, it can be improved.

Your original code is very close. Well done for use strict and putting $! in the die string. You should also always use warnings, use the three-parameter form of open, and use lexical file handles.
This program should help you.
use strict;
use warnings;
my %prv_line;
open (my $FILE, '<', 'somefile') || die $!;
while (<$FILE>) {
if ( $prv_line{$_} ) {
print "I saw this line $prv_line{$_} times\n";
}
$prv_line{$_}++;
}

Related

The diamond operator seems to work only once

I am writing a script in Perl where I have to open the same file twice in my code. This is my outline of the code:
#!/usr/bin/perl
use strict;
use warnings;
my %forward=();
my %reverse=();
while(<>){
chomp;
# store something
}
}
while(<>){ # open the same file again
chomp;
#print something
}
I am using the diamond operator so I am running the script like this
perl script.pl input.txt
But this is not producing any output. If I open the File using filehandle, the script works. What can be possibly wrong here?
Save your #ARGV before exhausting it. Of course, this will only work for actual files specified on the command line, and not with STDIN.
#!/usr/bin/env perl
use strict;
use warnings;
run(#ARGV);
sub run {
my #argv = #_;
first(#argv);
second(#argv);
}
sub first {
local #ARGV = #_;
print "First pass: $_" while <>;
}
sub second {
local #ARGV = #_;
print "Second pass: $_" while <>;
}
You read all there was to be read in the first loop, leaving nothing to read in the second.
If the input aren't huge, you can simply load it into memory.
my #lines = <>;
chomp( #lines );
for (#lines) {
...
}
for (#lines) {
...
}

Increment variables in files

I am a complete rookie with Perl. What I am trying to do is to open a list of files, increment three different variables in each file, save the files, and close.
The variables look like this
This_Is_My_Variable03
This_Is_My_Variable02
This_Is_My_Variable01
The variable ending in 01 is in the file multiple times. The variables are at times part of a Character string. the This_Is_My_Variable part of the variable never changes.
Thanks.
This may not be the best solution but it works
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
use warnings;
use strict;
use Tie::File;
tie my #data, 'Tie::File', 'myfile.txt' or die $!;
s/(This_Is_My_Variable)(\d+)+/$1.++($_=$2)/eg for #data;
untie #data;
Thank you Borodin for getting me started with Tie::File: that definitely helped.
Second solution using while loop
#!perl=C:\IBM\RationalSDLC\ClearCase\bin\perl
use warnings;
#use strict;
sub inc {
my ($num) = #_;
++$num;
}
open(FILE, "myfile.txt") || die $!;
$i = 0;
while (<FILE>) {
$string = $_;
if (/This_Is_My_Variable../) {
$string =~ s/(This_Is_My_Variable)(\d+)+/$1.++($_=$2)/eg;
print "$string \n";
$i++;
}
else {
print "$string \n";
}
}
close FILE;
Your "Second solution using while loop" has a number of problems.
Never disable use strict to get a program working. All that does is hide problems in your code
You have an unused subroutine inc and an unused variable $i
You should always use the three-parameter form of open, and lexical file handles
There is no need to test whether the line contains a string before applying a substitution
You can simply use ($2+1) in your replacement string, rather than assigning the value to $_ and incrementing it with ++($_=$2)
If you are going to use a named variable for the lines read from the file, then generally you should use while (my $string = <$fh>) {...}. For short blocks like this it is better just to use $_
You don't chomp the input, which would be fine except you are printing an additional space and newline after each line
You have print "$string \n" in your code twice. It may as well appear just once outside the if structure
This code performs the same process. I hope it helps.
use strict;
use warnings;
open(my $fh, '<', 'myfile.txt') || die $!;
while (<$fh>) {
s/(This_Is_My_Variable)(\d+)/$1.($2+1)/eg;
print;
}
use strict;
use warnings;
use Tie::File;
tie my #data, 'Tie::File', 'myfile' or die $!;
s/(\d+)$/sprintf '%02d', $1+1/e for #data;

Send file handle as argument in perl

Is it possible to send a file handle as an argument to a subroutine in PERL?
If yes, can you help with a sample code snippet showing how to receive it and use it in the subroutine?
You're using lexical variables (open(my $fh, ...)) as you should, right? If so, you don't have to do anything special.
sub f { my ($fh) = #_; print $fh "Hello, World!\n"; }
f($fh);
If you're using a glob (open(FH, ...)), just pass a reference to the glob.
f(\*STDOUT);
Though many places will also accept the glob itself.
f(*STDOUT);
Yes you can do it using .below is the sample code for the same.
#!/usr/bin/perl
use strict;
use warnings;
open (MYFILE, 'temp');
printit(\*MYFILE);
sub printit {
my $fh = shift;
while (<$fh>) {
print;
}
}
below is the test:
> cat temp
1
2
3
4
5
the perl script sample
> cat temp.pl
#!/usr/bin/perl
use strict;
use warnings;
open (MYFILE, 'temp');
printit(\*MYFILE);
sub printit {
my $fh = shift;
while (<$fh>) {
print;
}
}
execution
> temp.pl
1
2
3
4
5
>
Yes, like this:
some_func($fh, "hello");
where some_func is defined like this:
sub some_func {
my ($fh, $str) = #_;
print { $fh } "The message is: $str\n";
}

Program in Perl that reads from file, finds a line containing specific character and prints them. × 22510

I have been learning Perl for a few days and I am completely new.
The code is supposed to read from a big file and if a line contains "warning" it should store it and print it on a new line and also count the number of appearances of each type of warning. There are different types of warnings in the file e.g "warning GR145" or "warning GT10" etc.
So I want to print something like
Warning GR145 14 warnings
Warning GT10 12 warnings
and so on
The problem is that when I run it, it doesnt print the whole list of warnings.
I will appreciate your help. Here is the code:
use strict;
use warnings;
my #warnings;
open (my $file, '<', 'Warnings.txt') or die $!;
while (my $line = <$file>) {
if($line =~ /warning ([a-zA-Z0-9]*):/) {
push (#warnings, $line);
print $1 ,"\n";
}
}
close $file;
You are using case sensitive matching in your if statement. Try adding a /i:
if($line =~ /warning ([a-z0-9]*):/i)
EDIT: I misread the actual question, so this answer could be ignored...
You need to use a hash array, a mapping from warning string to occurrence count.
use strict;
use warnings;
my %warnings = {};
open (my $file, '<', 'Warnings.txt') or die $!;
while (my $line = <$file>) {
if ($line =~ /warning ([a-zA-Z0-9]*)\:.*/) {
++$warnings{$1};
}
}
close $file;
foreach $w (keys %warnings) {
print $w, ": ", $warnings{$w}, "\n";
}

Can anyone tell me where the bug is?

use strict;
use warnings;
open(FILE4,"cool.txt");
open(FILE6,">./mool.txt");
$line = <FILE4>;
while ($line ne "")
{
#array = split(/,/,$line);
$line = <FILE4> ;
print FILE6 ($array[0]);
print FILE6 ("\t");
print FILE6 ($array[1]);
print FILE6 ("\t");
print FILE6 ($array[2]);
}
These is the code I have written in perl. But the code is not working fine. Its giving tab space for every nextline. But i dont need that TAB space for every new line.Let me show you how the output is.
name contact email
samy 32344245 hyte#some.com
alex 231414124 coool#some.com
This is how i see my mool.txt file.The first line its working fine.But from the nextline I'm facing tab space.I am trying to find out where the bug is.Can anyone please let me know where the code is going wrong?I have gone through it so many times but unable to figure it out.Thank you
As someone asked im showing you the input file
"name","contact","email"
"samy","32344245","hyte#some.com"
You probably have whitespace at the beginning/end of lines in the input file.
Try stripping it with s///:
use strict;
use warnings;
open my $in, "<", "cool.txt" or die $!;
open my $out, ">", "mool.txt" or die $!;
while (my $line = <$in>) {
$line =~ s/^\s+|\s+$//g;
my #array = split(/,/, $line);
print $out join("\t", #array), "\n";
}
Put this statement inside the while loop.
chomp
This should be the first line after reading a line from a file. This will remove unwanted spaces.
use strict;
use warnings;
open(FILE4,"cool.txt");
open(FILE6,">./mool.txt");
while (<FILE4>)
{
chomp; #This will remove unwanted spaces
#array = split(/,/,$_); #$_ will represent the line which is read
print FILE6 ($array[0]);
print FILE6 ("\t");
print FILE6 ($array[1]);
print FILE6 ("\t");
print FILE6 ($array[2]);
print FILE6 ("\n");
}
What you seem to be doing is changing this file from comma-delimited to a tab-delimited. If so, this might be an easier way to do it:
while (<>) {
s/,/\t/g;
print;
}
And then use it like this:
$ script.pl cool.txt > mool.txt
You might even get away with a one-liner:
perl -pwe 's/,/\t/g' cool.txt > mool.txt
Or if you have weird whitespace:
perl -pwe 's/^\s+|\s+$//g; s/,/\t/g' cool.txt > mool.txt
Or, a safer version, using Text::CSV. This will handle complex data and such for you. If you get blank output, it may be your extra whitespace messing things up. If so, you can run the one-liner above without the s/,/\t/g line to get a "cleaned-up" version of the input file:
perl -pwe 's/^\s+|\s+$//g;' cool.txt > cool_clean.txt
Script:
use warnings;
use strict;
use Text::CSV;
use autodie;
my $csv_in = Text::CSV->new();
my $csv_out = Text::CSV->new( { sep_char => "\t", eol => "\n" } );
open my $fh, '<', 'data.csv';
open my $out, '>', 'mool.txt';
while (my $row = $csv_in->getline($fh)) {
$csv_out->print($out, $row);
}