perl unable to copy contents of file and print it - perl

I need to read/copy the contents of a file(test.pl) just as the way it is formatted and email it.
I am using the following code but I am unable to print anything out.
I get this error even though the file exists in the same directory.
Failed: No such file or directory
Code:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
open my $fh, '<', 'test.pl '
or die "Failed: $!\n";
my $text = do {
local $/;
<$fh>
};
close $fh
or die "Failed again: $!\n";
print $text, "\n";

It looks like there is an extra space in the filename you are trying to open. In your open statement, try changing 'test.pl ' to 'test.pl'.

if you are going to read files names from STDIN (user's input), you may want to trim them either by using regex (s/^\s+//....) or Text::Trim among other validations.

Related

How to modify content of a file using single file handle

I'm trying to modify content of a file using Perl.
The following script works fine.
#!/usr/bin/perl
use strict;
use warnings;
open(FH,"test.txt") || die "not able to open test.txt $!";
open(FH2,">","test_new.txt")|| die "not able to opne test_new.txt $!";
while(my $line = <FH>)
{
$line =~ s/perl/python/i;
print FH2 $line;
}
close(FH);
close(FH2);
The content of test.txt:
im learning perl
im in File handlers chapter
The output in test_new.txt:
im learning python
im in File handlers chapter
If I try to use same file handle for modifying the content of file, then I'm not getting expected output. The following is the script that attempts to do this:
#!/usr/bin/perl
use strict;
use warnings;
open(FH,"+<","test.txt") || die "not able to open test.txt $!";
while(my $line = <FH>)
{
$line =~ s/perl/python/i;
print FH $line;
}
close(FH);
Incorrect output in test.txt:
im learning perl
im learning python
chapter
chapter
How do I modify the file contents using single file handle?
You can't delete from a file (except at the end).
You can't insert characters into a file (except at the end).
You can replace a character in a file.
You can append to a file.
You can shorten a file.
That's it.
You're imagining you can simply replace "Perl" with "Python" in the file. Those aren't of the same length, so it would require inserting characters into the file, and you can't do that.
You can effectively insert characters into a file by loading the rest of the file into memory and writing it back out two characters further. But doing this gets tricky for very large files. It's also very slow since you end up copying a (possibly very large) portion of the file every time you want to insert characters.
The other problem with in-place modifications is that you can't recover from an error. If something happens, you'll be left with an incomplete or corrupted file.
If the file is small and you're ok with losing the data if something goes wrong, the simplest approach is to load the entire file into memory.
open(my $fh, '<+', $qfn)
or die("Can't open \"$qfn\": $!\n");
my $file = do { local $/; <$fh> };
$file =~ s/Perl/Python/g;
seek($fh, 0, SEEK_SET)
or die $!;
print($fh $file)
or die $!;
truncate($fh)
or die $!;
A safer approach is to write the data to a new file, then rename the file when you're done.
my $new_qfn = $qfn . ".tmp";
open(my $fh_in, '<', $qfn)
or die("Can't open \"$qfn\": $!\n");
open(my $fh_out, '<', $new_qfn)
or die("Can't create \"$new_qfn\": $!\n");
while (<$fh_in>) {
s/Perl/Python/g;
print($fh_out $_);
}
close($fh_in);
close($fh_out);
rename($qfn_new, $qfn)
or die $!;
The downside of this approach is it might change the file's permissions, and hardlinks will point to the old content instead of the new file. You also need permissions to create a file.
As #Сухой27 answered
it's typical situation that perl onliner pleasingly used.
perl -i -pe 's/perl/python/i'
perl takes below options
-p : make line by line loop(every line assign into $_ and print after evaluated $_)
-e : evaluate code block in above loop ( regex take $_ as default operand )
-i : in plcae file edit (if you pass arguments for -i, perl preserve original files with that extention)
if you run below script
perl -i.bak -pe 's/perl/python/i' test.txt
you will get modified test.txt
im learning python
im in File handlers chapter
and get original text files named in test.txt.bak
im learning perl
im in File handlers chapter

Perl multiple files read replacing string, write to multiple files

I have this code working, but this is only for one file with specific name, how can I let it does all .vb file in current folder and output with file name plus _1 in the back
#!/usr/bin/perl
use strict;
use warnings;
open my $fhIn, '<', 'file.vb' or die $!;
open my $fhOut, '>', 'file_1.vb' or die $!;
while (<$fhIn>) {
print $fhOut "'01/20/2016 Added \ngpFrmPosition = Me.Location\n" if /MessageBox/ and !/'/;
print $fhOut $_;
}
close $fhOut;
close $fhIn;
I might approach it like this. (This assumes the script is running in the same directory as the .vb files).
#!/usr/bin/perl
use strict;
use warnings;
# script running in same directory as the .vb files
for my $file (glob "*.vb") {
my $outfile = $file =~ s/(?=\.vb$)/_1/r;
print "$file $outfile\n"; # DEBUG
# open input and output files
# do the while loop
}
The print statement in the loop is for debug purposes - to see if you are creating the new file names correctly. You can delete it or comment it out when you are satisfied you have got the files you want.
Update: Put the glob in the for loop instead of reading it to an array.

Perl not able to tail rotated log file

I am using property based filters of rsyslog to send specific logs to seperate file where those logs will be parsed using perl.
This is my rsyslog entry
$template SPLIT,"/home/shivam/hello-%$YEAR%%$MONTH%%$DAY%%$HOUR%%$MINUTE%"
:msg, contains, "hello" -?SPLIT
So rsyslog will create separate files for logs coming after every minute. Files will be created like this hello-201505281139.
My perl script to parse these files is
use strict;
use warnings;
use POSIX qw(strftime);
my $date = strftime "%Y%m%d%H%M", localtime;
my $file = '/root/defer-'.$date;
open(my $fh,'<',$file) or die "unable to open file $!\n";
while(1) {
while(my $line = <$fh>){
print "$date\n";
print "$line";
}
sleep(2);
unless ($date == strftime "%Y%m%d%H%M", localtime) {
close($fh);
$date = strftime "%Y%m%d%H%M", localtime;
$file = '/root/defer-'. $date;
system("touch $file");
open(my $fh,'<',$file) or die "unable to open file $!\n";
}
}
In unless block i am checking that if minute has changed then i close previous file and open new file.
The reason i am creating new file from script and not waiting for rsyslog to create file is that the frequency of logs coming is not that much. So i create file and just start reading on it in hope that when any new log will come i will be able to read that.
But what is happening is that i am able to create new file but not able to read anything from that file.
This is what i am getting as warning
readline() on closed filehandle $fh at test.pl line 14.
Line 14 in my code is this line
while(my $line = <$fh>){
I am not able to see anything wrong in my code. Please suggest what is the mistake.
You have two different $fh lexical (my) variables,
So instead declaring new one
open(my $fh,'<',$file) or die "unable to open file $!\n";
keep using previously declared one,
open($fh,'<',$file) or die "unable to open file $!\n";

Write to a file in Perl

Consider:
#!/usr/local/bin/perl
$files = "C:\\Users\\A\\workspace\\CCoverage\\backup.txt";
unlink ($files);
open (OUTFILE, '>>$files');
print OUTFILE "Something\n";
close (OUTFILE);
The above is a simple subroutine I wrote in Perl, but it doesn't seem to work. How can I make it work?
Variables are interpolated only in strings using double quotes ". If you use single quotes ' the $ will be interpreted as a dollar.
Try with ">>$files" instead of '>>$files'
Always use
use strict;
use warnings;
It will help to get some more warnings.
In any case also declare variables
my $files = "...";
You should also check the return value of open:
open OUTFILE, ">>$files"
or die "Error opening $files: $!";
Edit: As suggested in the comments, a version with the three arguments open and a couple of other possible improvements
#!/usr/bin/perl
use strict;
use warnings;
# warn user (from perspective of caller)
use Carp;
# use nice English (or awk) names for ugly punctuation variables
use English qw(-no_match_vars);
# declare variables
my $files = 'example.txt';
# check if the file exists
if (-f $files) {
unlink $files
or croak "Cannot delete $files: $!";
}
# use a variable for the file handle
my $OUTFILE;
# use the three arguments version of open
# and check for errors
open $OUTFILE, '>>', $files
or croak "Cannot open $files: $OS_ERROR";
# you can check for errors (e.g., if after opening the disk gets full)
print { $OUTFILE } "Something\n"
or croak "Cannot write to $files: $OS_ERROR";
# check for errors
close $OUTFILE
or croak "Cannot close $files: $OS_ERROR";

filehandle - won't write to a file

I cannot get the script below to write to the file, data.txt, using a FILEHANDLE. Both the files are in the same folder, so that's not the issue. Since I started with Perl, I have noticed to run scripts, I have to use a full path: c:\programs\scriptname.pl and also the same method to input files. I thought that could be the issue and tried this syntax below but that didn't work either...
open(WRITE, ">c:\programs\data.txt") || die "Unable to open file data.txt: $!";
Here is my script. I have checked the syntax until it makes me crazy and cannot see an issue. Any help would be greatly appreciated!. I'm also puzzled, why the die function hasn't kicked in.
#!c:\strawberry\perl\bin\perl.exe
#strict
#diagnostics
#warnings
#obtain info in variables to be written to data.txt
print("What is your name?");
$name = <STDIN>;
print("How old are you?");
$age = <STDIN>;
print("What is your email address?");
$email = <STDIN>;
#data.txt is in the same file as this file.
open(WRITE, ">data.txt") || die "Unable to open file data.txt: $!";
#print information to data.txt
print WRITE "Hi, $name, you are \s $age and your email is \s $email";
#close the connection
close(WRITE);
How I solved this problem solved.
I have Strawberry Perl perl.exe installed on the c: drive, installed through and using the installer with a folder also on c with my scripts in, which meant I couldn't red/write to a file (directional or using functions, ie the open one) and I always had to use full paths to launch a script. I solved this problem after a suggestion of leaving the interpreter installed where it was and moving my scripts file to the desktop (leave the OS command in the first line of the script where it is as the interpreter is still in the same place it was initially). Now I can run the scripts with one click and read/write and append to file with CMD prompt and using Perl functions with ease.
Backslashes have a special meaning in double-quoted strings. Try escaping the backslashes.
open(WRITE, ">c:\\programs\\data.txt") || die ...;
Or, as you're not interpolating variables, switch to single quotes.
open(WRITE, '>c:\programs\data.txt') || die ...;
It's also worth using the three-argument version of open and lexical filehandles.
open(my $write_fh, '>', 'c:\programs\data.txt') || die ...;
you must use "/" to ensure portability, so: open(WRITE, ">c:/programs/data.txt")
Note: I assume that c:/programs folder exists
You may want to try FindBin.
use strict;
use warnings;
use autodie; # open will now die on failure
use FindBin;
use File::Spec::Functions 'catfile';
my $filename = catfile $FindBin::Bin, 'data.txt';
#obtain info in variables to be written to data.txt
print("What is your name?"); my $name = <STDIN>;
print("How old are you?"); my $age = <STDIN>;
print("What is your email address?"); my $email = <STDIN>;
{
open( my $fh, '>', $filename );
print {$fh} "Hi, $name, you are $age, and your email is $email\n";
close $fh;
}
If you have an access problem when you try to print to data.txt you can change that line to:
print WRITE "Hi, $name, you are \s $age and your email is \s $email" || die $!;
to get more information. A read only file will cause this error message:
Unable to open file data.txt: Permission denied at perl.pl line 12, <STDIN> line 3.