Why is this printing twice in perl? - perl

So I'm hoping somebody can just explain to my why when I run the following code, it prints ".link/output" at both the beginning and end of the line. I was trying to get it to print only at the end of the line. Any thoughts?
#!/usr/local/bin/perl
use warnings;
use strict;
my $logfiles = $ARGV[0]; #file containing the list of all the log file names
my #logf = ();
my $i;
open (F2, "<", $logfiles);
while(<F2>){
#logf = $_;
foreach $i(#logf){
print $_.".link/output";
}
}
close F2;
So for example, if the file I'm reading in is:
cat
dog
I want to see:
cat.link/output
dog.link/output
But isntead I am getting:
.link/outputcat.link/output
.link/outputdog.link/output
Could anybody please explain to me why this is happening and/or how to fix it? Thank you.

you have an empty element at the beginning of your list. simply shift #logf

I don't see what #logf does. Couldn't you just do this:
#!/usr/bin/env perl
use warnings;
use strict;
my $logfiles = $ARGV[0]; #file containing the list of all the log file names
#open(my $f2, "<", $logfiles);
# FOR TESTING, use above in your code
my $f2 = \*DATA;
# ===========
while(<$f2>){
chomp;
print "$_.link/output\n";
}
__DATA__
cat
dog

Related

Delete date and time from text file

I have a text file having date and time in below mentioned format at the 4th line of the file:
[0x1FFD] LOG 2017/02/22 06:20:48.644 Diagnostic Version Length: 0149 255
Now, I have to delete the string "2017/02/22 06:20:48.644"in the file.
This date and time is not constant and will change whenever I save the file(it takes the current date and time).
As I am not a perl coder , I am finding it difficult to figure out the way.
NOTE: I need to make changes in input file only. I don't need to create a seperate output file.
Thanks in advance!
use strict;
use warnings;
my $str = " [0x1FFD] LOG 2017/02/22 06:20:48.644 Diagnostic Version and more stuff";
$str =~ s|\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}\.\d{3}||;
print $str;
if it is in a file you need to loop through the file and print each line to exclude the date.
Like this
use strict;
use warnings;
open FILE, "<", "filename.log" or die $!;
my #list = <FILE>;
foreach my $str(#list) {
$str =~ s|\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}\.\d{3}||;
print $str;
}
close(FILE);
So from there you can figure out how to write it back to the original file. :)
You can use the Tie::File module to update the input file at single call.
use warnings;
use strict;
use Tie::File;
my $str = 'data1.txt';
tie my #lines, 'Tie::File', $str or die $!;
my $joinLines = join "\n", #lines;
Either use #1 or #2 for based on the regex modification
#1. $joinLines=~s/(LOG\s)(.*?)(\sDiagnostic Version)/$1$3/g;
#2. $joinLines =~ s/\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}\.\d{3}//;
print $joinLines;
#lines = split /\n/, $joinLines;
untie #lines;
Please check and test at your end.

File::Temp pass system command output to temp file

I'm trying to capture the output of a tail command to a temp file.
here is a sample of my apache access log
Here is what I have tried so far.
#!/usr/bin/perl
use strict;
use warnings;
use File::Temp ();
use File::Temp qw/ :seekable /;
chomp($tail = `tail access.log`);
my $tmp = File::Temp->new( UNLINK => 0, SUFFIX => '.dat' );
print $tmp "Some data\n";
print "Filename is $tmp\n";
I'm not sure how I can go about passing the output of $tail to this temporoy file.
Thanks
I would use a different approach for tailing the file. Have a look to File::Tail, I think it will simplify things.
It sounds like all you need is
print $tmp $tail;
But you also need to declare $tail and you probably shouldn't chomp it, so
my $tail = `tail access.log`;
Is classic Perl approach to use the proper filename for the handle?
if(open LOGFILE, 'tail /some/log/file |' and open TAIL, '>/tmp/logtail')
{
print LOGFILE "$_\n" while <TAIL>;
close TAIL and close LOGFILE
}
There is many ways to do this but since you are happy to use modules, you might as well use File::Tail;
use v5.12;
use warnings 'all';
use File::Tail;
my $lines_required = 10;
my $out_file = "output.txt";
open(my $out, '>', $out_file) or die "$out_file: $!\n";
my $tail = File::Tail->new("/some/log/file");
for (1 .. $lines_required) {
print $out $tail->read;
}
close $out;
This sits and monitors the log file until it gets the 10 new lines. If you just want a copy of the last 10 lines as is, the easiest way is to use I/O redirection from the shell: tail /some/log/file > my_copy.txt

PERL: repeated lines

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{$_}++;
}

Parsing the large files in Perl

I need to compare the big file(2GB) contains 22 million lines with the another file. its taking more time to process it while using Tie::File.so i have done it through 'while' but problem remains. see my code below...
use strict;
use Tie::File;
# use warnings;
my #arr;
# tie #arr, 'Tie::File', 'title_Nov19.txt';
# open(IT,"<title_Nov19.txt");
# my #arr=<IT>;
# close(IT);
open(RE,">>res.txt");
open(IN,"<input.txt");
while(my $data=<IN>){
chomp($data);
print"$data\n";
my $occ=0;
open(IT,"<title_Nov19.txt");
while(my $line2=<IT>){
my $line=$line2;
chomp($line);
if($line=~m/\b$data\b/is){
$occ++;
}
}
print RE"$data\t$occ\n";
}
close(IT);
close(IN);
close(RE);
so help me to reduce it...
Lots of things wrong with this.
Asides from the usual (lack of use strict, use warnings, use of 2-argument open(), not checking open() result, use of global filehandles), the specific problem in your case is that you are opening/reading/closing the second file once for every single line of the first. This is going to be very slow.
I suggest you open the file title_Nov19.txt once, read all the lines into an array or hash or something, then close it; and then you can open the first file, input.txt and walk along that once, comparing to things in the array so you don't have to reopen that second file all the time.
Futher I suggest you read some basic articles on style/etc.. as your question is likely to gain more attention if it's actually written in vaguely modern standards.
I tried to build a small example script with a better structure but I have to say, man, your problem description is really very unclear. It's important to not read the whole comparison file each time as #LeoNerd explained in his answer. Then I use a hash to keep track of the match count:
#!/usr/bin/env perl
use strict;
use warnings;
# cache all lines of the comparison file
open my $comp_file, '<', 'input.txt' or die "input.txt: $!\n";
chomp (my #comparison = <$comp_file>);
close $comp_file;
# prepare comparison
open my $input, '<', 'title_Nov19.txt' or die "title_Nov19.txt: $!\n";
my %count = ();
# compare each line
while (my $title = <$input>) {
chomp $title;
# iterate comparison strings
foreach my $comp (#comparison) {
$count{$comp}++ if $title =~ /\b$comp\b/i;
}
}
# done
close $input;
# output (sorted by count)
open my $output, '>>', 'res.txt' or die "res.txt: $!\n";
foreach my $comp (#comparison) {
print $output "$comp\t$count{$comp}\n";
}
close $output;
Just to get you started... If someone wants to further work on this: these were my test files:
title_Nov19.txt
This is the foo title
Wow, we have bar too
Nothing special here but foo
OMG, the last title! And Foo again!
input.txt
foo
bar
And the result of the program was written to res.txt:
foo 3
bar 1
Here's another option using memowe's (thank you) data:
use strict;
use warnings;
use File::Slurp qw/read_file write_file/;
my %count;
my $regex = join '|', map { chomp; $_ = "\Q$_\E" } read_file 'input.txt';
for ( read_file 'title_Nov19.txt' ) {
my %seen;
!$seen{ lc $1 }++ and $count{ lc $1 }++ while /\b($regex)\b/ig;
}
write_file 'res.txt', map "$_\t$count{$_}\n",
sort { $count{$b} <=> $count{$a} } keys %count;
Numerically-sorted output to res.txt:
foo 3
bar 1
An alternation regex which quotes meta characters (\Q$_\E) is built and used, so only one pass against the large file's lines is needed. The hash %seen is used to insure that the input words are only counted once per line.
Hope this helps!
Try this:
grep -i -c -w -f input.txt title_Nov19.txt > res.txt

Adding same text to all files of the directory

I have one folder. There are 32 files and 3 directories in that folder. I want to add some lines of text on each file at top. How can I do that?
Use File::Find to find the files. Use Tie::File and unshift to add lines to the top of the file.
TLP already told you some hints how to solve the Problem. But there is always more then one way to do it. Instead of File::Find and Tie::File i would use some more "modern" modules. In these full example i use Path::Class::Rule with an iterative interface instead of an recursive interface that i like more.
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use open ':encoding(UTF-8)';
use open ':std';
use Path::Class;
use Path::Class::Rule;
my $rule = Path::Class::Rule->new->file;
my $iter = $rule->iter(dir('test'));
while ( my $file = $iter->() ) {
print $file->stringify, "\n";
add_line_to_file($file, "Sid was here.\n");
}
# 1: Path::Class::File Object
# 2: The Line
sub add_line_to_file {
my ( $file, $line ) = #_;
# Open File - return IO::File object
my $fh = $file->open('>>') or die "Cannot open $file: $!\n";
# Seek to end
$fh->seek(0, 2);
# Add line
$fh->print($line);
$fh->close;
return;
}
This could work:
perl -pi -e 's/^/my text\n/' * */*
Please try this on copy of your directory to make sure it does what you want.