Perl-Copying file from one location to other but content not copying - perl

I am writing a script in perl where I am creating a file and getting input from user for file but when I am copying that file to other location the file is copying but it is empty only. My code is
# !/usr/bin/perl -w
for($i = 1;$i<5;$i++)
{
open(file1,"</u/man/fr$i.txt");
print "Enter text for file $i";
$txt = <STDIN>;
print file1 $txt;
open(file2,">/u/man/result/fr$i.txt");
while(<file1>)
{
print file2 $_;
}
close(file1);
close(file2);
}
fr1 to fr4 are creating but these are empty. like when I run my code it is asking for input i provide the input and code run without error but still the files are empty. Please help.
in line number 4 I changed < to > also as I thought for creating new file it might need that but still it is not working

You need to close the filehandle that was written to in order to be able to read from that file.
use warnings;
use strict;
use feature 'say';
for my $i (1..4)
{
my $file = "file_$i.txt";
open my $fh, '>', $file or die "Can't open $file: $!";
say $fh "Written to $file";
# Opening the same filehandle first *closes* it if already open
open $fh, '<', $file or die "Can't open $file: $!";
my $copy = "copy_$i.txt";
open my $fh_cp, '>', $copy or die "Can't open $copy: $!";
while (<$fh>) {
print $fh_cp $_;
}
close $fh_cp; # in case of early errors in later iterations
close $fh;
}
This creates the four files, file_1.txt etc, and their copies, copy_1.txt etc.
Please note the compulsory checking whether open worked.

You can't write to a filehandle that's not open for writing. You can't read from a filehandle that's not open for reading. Never ignore the return value of open.
# !/usr/bin/perl
use warnings; # Be warned about mistakes.
use strict; # Prohibit stupid things.
for my $i (1 .. 4) { # lexical variable, range
open my $FH1, '>', "/u/man/fr$i.txt" # 3 argument open, lexical filehandle, open for writing
or die "$i: $!"; # Checking the return value of open
print "Enter text for file $i: ";
my $txt = <STDIN>;
print {$FH1} $txt;
open my $FH2, '<', "/u/man/fr$i.txt" # Reopen for reading.
or die "$i: $!";
open my $FH3, '>', "/u/man/result/fr$i.txt" or die "$i: $!";
while (<$FH2>) {
print {$FH3} $_;
}
close $FH3;
}

I opened the file in write mode using filehandler1 Then i again opened the file in read mode using same filehandler1 then I opened filehandler2 for destiantion So it is working fine for me then.

system("cp myfile1.txt /somedir/myfile2.txt")
`cp myfile1.txt /somedir/myfile2.txt`

Related

Counting number of lines with conditions

This is my script count.pl, I am trying to count the number of lines in a file.
The script's code :
chdir $filepath;
if (-e "$filepath"){
$total = `wc -l < file.list`;
printf "there are $total number of lines in file.list";
}
i can get a correct output, but i do not want to count blank lines and anything in the file that start with #. any idea ?
As this is a Perl program already open the file and read it, filtering out lines that don't count with
open my $fh, '<', $filename or die "Can't open $filename: $!";
my $num_lines = grep { not /^$|^\s*#/ } <$fh>;
where $filename is "file.list." If by "blank lines" you mean also lines with spaces only then chagne regex to /^\s*$|^\s*#/. See grep, and perlretut for regex used in its condition.
That filehandle $fh gets closed when the control exits the current scope, or add close $fh; after the file isn't needed for processing any more. Or, wrap it in a block with do
my $num_lines = do {
open my $fh, '<', $filename or die "Can't open $filename: $!";
grep { not /^$|^\s*#/ } <$fh>;
};
This makes sense doing if the sole purpose of opening that file is counting lines.
Another thing though: an operation like chdir should always be checked, and then there is no need for the race-sensitive if (-e $filepath) either. Altogether
# Perhaps save the old cwd first so to be able to return to it later
#my $old_cwd = Cwd::cwd;
chdir $filepath or die "Can't chdir to $filepath: $!";
open my $fh, '<', $filename or die "Can't open $filename: $!";
my $num_lines = grep { not /^$|^\s*#/ } <$fh>;
A couple of other notes:
There is no reason for printf. For all normal prints use say, for which you need use feature qw(say); at the beginning of the program. See feature pragma
Just in case, allow me to add: every program must have at the beginning
use warnings;
use strict;
Perhaps the original intent of the code in the question is to allow a program to try a non-existing location, and not die? In any case, one way to keep the -e test, as asked for
#my $old_cwd = Cwd::cwd;
chdir $filepath or warn "Can't chdir to $filepath: $!";
my $num_lines;
if (-e $filepath) {
open my $fh, '<', $filename or die "Can't open $filename: $!";
$num_lines = grep { not /^$|^\s*#/ } <$fh>;
}
where I still added a warning if chdir fails. Remove that if you really don't want it. I also added a declaration of the variable that is assigned the number of lines, with my $total_lines;. If it is declared earlier in your real code then of course remove that line here.
perl -ne '$n++ unless /^$|^#/ or eof; print "$n\n" if eof'
Works with multiple files too.
perl -ne '$n++ unless /^$|^#/ or eof; END {print "$n\n"}'
Better for a single file.
open(my $fh, '<', $filename);
my $n = 0;
for(<$fh>) { $n++ unless /^$|^#/}
print $n;
Using sed to filter out the "unwanted" lines in a single file:
sed '/^\s*#/d;/^\s*$/d' infile | wc -l
Obviously, you can also replace infile with a list of files.
The solution is very simple, no any magic.
use strict;
use warnings;
use feature 'say';
my $count = 0;
while( <> ) {
$count++ unless /^\s*$|^\s*#/;
}
say "Total $count lines";
Reference:
<>

Printing a content of a file to the screen in perl

I Have a perl script which write a few lines into file. (I checked and see that the file is written correctly)
right after that I want to print the content to the screen, the way I'm trying to do it- is to read the file and print it
open (FILE, '>', "tmpLogFile.txt") or die "could not open the log file\n";
$aaa = <FILE>;
close (FILE);
print $aaa;
but I get nothing on the screen, what do I do wrong?
To read you need to specify the open mode as <.
Also, $aaa = <FILE> has scalar context, and only reads a line.
Using print <FILE> you can have list context and read all lines:
open (FILE, '<', "tmpLogFile.txt") or die "could not open the log file\n";
print <FILE>;
close (FILE);
try this:
use strict;
use warnings;
my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
or die "Could not open file '$filename' $!";
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
print "done\n"

Read Increment Then Write to a text file in perl

I have this little perl script which opens a txt file, reads the number in it, then overwrites the file with the number incremented by 1. I can open and read from the file, I can write to the file but I"m having issues overwriting. In addition, I'm wondering if there is a way to do this without opening the file twice. Here's my code:
#!/usr/bin/perl
open (FILE, "<", "data.txt") or die "$! error trying to a\
ppend";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number++;
print $myNumber+'\n';
close(FILE);
open(FILE, ">data.txt") or die "$! error";
print FILE $myNumber;
close(FILE);
Change the line
$myNumber = $number++;
to
$myNumber = $number+1;
That should solve the problem.
Below is how you could do by opening the file just once:
open(FILE, "+<data.txt") or die "$! error";
undef $/;
$number = <FILE>;
$number = int($number);
$myNumber = $number+1;
seek(FILE, 0, 0);
truncate(FILE, tell FILE);
print $myNumber+"\n";
print FILE $myNumber;
close(FILE);
It's good that you used the three-argument form of open the first time. You also needed to do that in your second open. Also, you should use lexical variables, i.e., those which begin with my, in your script--even for your file handles.
You can just increment the variable that holds the number, instead of passing it to a new variable. Also, it's a good idea to use chomp. This things being said, consider the following option:
#!/usr/bin/env perl
use strict;
use warnings;
undef $/;
open my $fhIN, "<", "data.txt" or die "Error trying to open for reading: $!";
chomp( my $number = <$fhIN> );
close $fhIN;
$number++;
open my $fhOUT, ">", "data.txt" or die "Error trying to open for writing: $!";
print $fhOUT $number;
close $fhOUT;
Another option is to use the Module File::Slurp, letting it handle all the I/O operations:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Slurp qw/edit_file/;
edit_file { chomp; $_++ } 'data.txt';
Try this:
#!/usr/bin/perl
use strict;
use warnings;
my $file = "data.txt";
my $number = 0;
my $fh;
if( -e $file ) {
open $fh, "+<", $file or die "Opening '$file' failed, because $!\n";
$number = <$fh>;
seek( $fh, 0, 0 );
} else { # if no data.txt exists - yet
open $fh, ">", $file or die "Creating '$file' failed, because $!\n";
}
$number++;
print "$number\n";
print $fh $number;
close( $fh );
If you're using a bash shell, and you save the code to test.pl, you can test it with:
for i in {1..10}; do ./test.pl; done
Then 'cat data.txt', should show a 10.

Read all files in a directory in perl

in the first part of my code, I read a file and store different parts of it in different files in a directory and in the following I wanna read all the files in that directory that I build it in the first part of code:
while(<file>){
#making files in directory Dir
}
opendir(Dir, $indirname) or die "cannot open directory $indirname";
#docs = grep(/\.txt$/,readdir(Dir));
foreach $d (#Dir) {
$rdir="$indirname/$d";
open (res,$rdir) or die "could not open $rdir";
while(<res>){
}
but with this code, the last line of the last file wont be read
As I don't know what you are doing in the line reading loop and don't understand #docs and #Dir, I'll show code that 'works' for me:
use strict;
use warnings;
use English;
my $dir = './_tmp/readFID';
foreach my $fp (glob("$dir/*.txt")) {
printf "%s\n", $fp;
open my $fh, "<", $fp or die "can't read open '$fp': $OS_ERROR";
while (<$fh>) {
printf " %s", $_;
}
close $fh or die "can't read close '$fp': $OS_ERROR";
}
output:
./_tmp/readFID/123.txt
1
2
3
./_tmp/readFID/45.txt
4
5
./_tmp/readFID/678.txt
6
7
8
Perhaps you can spot a relevant difference to your script.
I modified the code slightly to just test the basic idea in a directory containing my perl programs and it does seem to work. You should be iterating through #docs instead of #dir though (and I highly recommend using both the strict and warnings pragmas).
opendir(DIR, ".") or die "cannot open directory";
#docs = grep(/\.pl$/,readdir(DIR));
foreach $file (#docs) {
open (RES, $file) or die "could not open $file\n";
while(<RES>){
print "$_";
}
}
glob does what you want, without the open/close stuff. And once you stick a group of files into #ARGV the "diamond" operator works as normal.
#ARGV = <$indirname/*.txt>;
while ( <> ) {
...
}

perl: Writing file at Nth position

I am trying to write in to file at Nth POSITION. I have tried with below example but it writes at the end. Please help to achieve this.
#!/usr/bin/perl
open(FILE,"+>>try.txt")
or
die ("Cant open file try.txt");
$POS=5;
seek(FILE,$POS,0);
print FILE "CP1";
You are opening the file in read-write appending mode. Try opening the file in read-write mode:
my $file = "try.txt";
open my $fh, "+<", $file
or die "could not open $file: $!";
Also, note the use of the three argument open, the lexical filehandle, and $!.
#!/usr/bin/perl
use strict;
use warnings;
#create an in-memory file
my $fakefile = "1234567890\n";
open my $fh, "+<", \$fakefile
or die "Cant open file: $!";
my $offset = 5;
seek $fh, $offset, 0
or die "could not seek: $!";
print $fh "CP1";
print $fakefile;
The code above prints:
12345CP190
If I understand you correctly, if the file contents are
123456789
you want to change that to
1234CP157689
You cannot achieve that using modes supplied to open (regardless of programming language).
You need to open the source file and another temporary file (see File::Temp. Read up to the insertion point from the source and write the contents to the temporary file, write what you want to insert, then write the remainder of the source file to the temporary file, close the source and rename the temporary to the source.
If you are going to do this using seek, both files must be opened in binary mode.
Here is an example using line oriented input and text mode:
#!/usr/bin/perl
use strict; use warnings;
use File::Temp qw( :POSIX );
my $source = 'test.test';
my $temp = tmpnam;
open my $source_h, '<', $source
or die "Failed to open '$source': $!";
open my $temp_h, '>', $temp
or die "Failed to open '$temp' for writing: $!";
while ( my $line = <$source_h> ) {
if ( $line =~ /^[0-9]+$/ ) {
$line = substr($line, 0, 5) . "CP1" . substr($line, 5);
}
print $temp_h $line;
}
close $temp_h
or die "Failed to close '$temp': $!";
close $source_h
or die "Failed to close '$source': $!";
rename $temp => $source
or die "Failed to rename '$temp' to '$source': $!";
this works for me
use strict;
use warnings;
open( my $fh, '+<', 'foo.txt' ) or die $!;
seek( $fh, 3, 0 );
print $fh "WH00t?";
this is also a more "modern" use of open(), see http://perldoc.perl.org/functions/open.html
The file will be closed when $fh goes out of scope ..
"Inserting" a string into a function can (mostly) be done in place. See the lightly used truncate built-in function.
open my $fh, '+<', $file or die $!;
seek $fh, 5, 0;
$/ = undef;
$x = <$fh>; # read everything after the 5th byte into $x
truncate $fh, 5;
print $fh "CPI";
print $fh $x;
close $fh;
If your file is line or record oriented, you can insert lines or modify individual lines easily with the core module Tie::File This will allow the file to be treated as an array and Perl string and array manipulation to be used to modify the file in memory. You can safely operate on huge files larger than your RAM with this method.
Here is an example:
use strict; use warnings;
use Tie::File;
#create the default .txt file:
open (my $out, '>', "nums.txt") or die $!;
while(<DATA>) { print $out "$_"; }
close $out or die $!;
tie my #data, 'Tie::File', "nums.txt" or die $!;
my $offset=5;
my $insert="INSERTED";
#insert in a string:
$data[0]=substr($data[0],0,$offset).$insert.substr($data[0],$offset)
if (length($data[0])>$offset);
#insert a new array element that becomes a new file line:
splice #data,$offset,0,join(':',split(//,$insert));
#insert vertically:
$data[$_]=substr($data[$_],0,$offset) .
substr(lc $insert,$_,1) .
substr($data[$_],$offset) for (0..length($insert));
untie #data; #close the file too...
__DATA__
123456789
234567891
345678912
456789123
567891234
678912345
789123456
891234567
912345678
Output:
12345iINSERTED6789
23456n7891
34567s8912
45678e9123
56789r1234
I:N:St:E:R:T:E:D
67891e2345
78912d3456
891234567
912345678
The file modifications with Tie::File are made in place and as the array is modified. You could use Tie::File just on the first line of you file to modify and insert as you requested. You can put sleep between the array mods and use tail -n +0 -f on the file and watch the file change if you wish...
Alternatively, if your file is reasonable size and you want to treat it like characters, you can read the entire file into memory, do string operations on the data, then write the modified data back out. Consider:
use strict; use warnings;
#creat the default .txt file:
open (my $out, '>', "nums.txt") or die $!;
while(<DATA>) { print $out "$_"; }
close $out or die $!;
my $data;
open (my $in, '<', "nums.txt") or die $!;
{ local $/=undef; $data=<$in>; }
close $in or die $!;
my $offset=5;
my $insert="INSERTED";
open (my $out, '>', "nums.txt") or die $!;
print $out substr($data,0,$offset).$insert.substr($data,$offset);
close $out or die $!;
__DATA__
123456789
2
3
4
5
6
7
8
9
Output:
12345INSERTED6789
2
3
4
5
6
7
8
9
If you treat files as characters, beware that under Windows, files in text mode have a \r\n for a new line. That is two characters if opened in binary mode.