File not getting copied in perl - perl

File "/root/actual" is not getting over written with content of "/root/temp" via perl script. If manually edited "/root/actual" is getting modified.
copy("/root/actual","/root/temp") or die "Copy failed: $!";
open(FILE, "</root/temp") || die "File not found";
my #lines = <FILE>;
close(FILE);
my #newlines;
foreach(#lines) {
$_ =~ s/$aref1[0]/$profile_name/;
push(#newlines,$_);
}
open(FILE, ">/root/actual") || die "File not found";
print FILE #newlines;
close(FILE);

File "/root/actual" is not getting over written with content of "/root/temp" via perl script. If manually edited "/root/actual" is getting modified.
Do you mean that /root/temp isn't being replaced by /root/actual? Or is /root/temp being modified as you wish, but it's not copying over /root/acutual at the end of your program?
I suggest that you read up on modern Perl programming practices. You need to have use warnings; and use strict; in your program. In fact, many people on this forum won't bother answering Perl questions unless use strict; and use warnings; are used.
Where is $aref1[0] coming from? I don't see #aref1 declared anywhere in your program. Or, for that matter $profile_name.
If you're reading in the entire file into a regular expression, there's no reason to copy it over to a temporary file first.
I rewrote what you had in a more modern syntax:
use strict;
use warnings;
use autodie;
use constant {
FILE_NAME => 'test.txt',
};
my $profile_name = "bar"; #Taking a guess
my #aref1 = qw(foo ??? ??? ???); #Taking a guess
open my $input_fh, "<", FILE_NAME;
my #lines = <$input_fh>;
close $input_fh;
for my $line ( #lines ) {
$line =~ s/$aref1[0]/$profile_name/;
}
open my $output_fh, ">", FILE_NAME;
print ${output_fh} #lines;
close $output_fh;
This works.
Notes:
use autodie; means you don't have to check whether files opened.
When I use a for loop, I can do inplace replacing in an array. Each item is a pointer to that entry in the array.
No need for copy or a temporary file since you're replacing the original file anyway.
I didn't use it here since you didn't, but map { s/$aref1[0]/$profile_name/ } #lines; can replace that for loop. See map.

Related

print specific INFILE area using perl

I have a file with the format below
locale,English,en_AU,6251
locale,French,fr_BE,25477
charmap,English,EN,5423
And I would like to use perl to print out something with the option "-a" follows by the file and outputs something like
Available locales:
en_Au
fr_BE
EN
To do that, I have the perl script below
$o = $ARGV[0];
$f = $ARGV[1];
open (INFILE, "<$f") or die "error";
my $line = <INFILE>;
my #fields = split(',', $line);
if($o eq "-a"){
if(!$fields[2]){print "No locales available\n";}
else{print "Available locales: \n";
while($fields[2]){print "$fields[2]\n";}
}
}
close(INFILE);
And I have three questions here.
1. my script will only print the first locale "en_Au" forever.
2. it should be able to test if a file is empty, but if a file is purely empty, it outputs nothing, but if I type in two empty lines in the file, it prints two lines of "No locales available" instead.
3.In fact in the (!$filed[2]) part I should verify if the file is empty or no available locales exist, if so do I need to put some regular expression here to verify if it is a locale as well??
Hope someone could help me figure these out! Many thanks!!!
The biggest missing thing is a loop over lines from the file, in which you then process one line at a time. Comments follow the code.
use warnings;
use strict;
use feature 'say';
use Getopt::Long;
#my ($opt, $file) = #ARGV; # better use a module
my ($opt, $file);
Getoptions( 'a' => \$opt, 'file=s' => \$file ) or usage();
usage() if not $file; # mandatory argument
open my $fh, '<', $file or die "Can't open $file: $!";
while (my $line = <$fh>) {
chomp $line;
my #fields = split /,/, $line;
next if not $fields[2];
if ($opt) {
say $fields[2];
}
}
close $fh;
sub usage {
say STDERR "Usage: $0 [-a] --file filename";
exit 1;
}
This prints the desired output. (Is that simple condition on $fields[2] really all you need?)
Comments
Always have use warnings; and use strict; at the beginning
I do not recommend single-letter variable names. One forgets what they mean, it makes the code harder to follow, and it's way too easy to make silly mistakes
The #ARGV can be assigned to variables in a list. Much better, use Getopt::Long module, which checks invocation and allows for far easier interface changes. I set the -a option to act as a "flag," so it just sets a variable ($opt) if it's given. If that should have possible values instead, use 'a=s' => \$opt and check for a value.
Use lexical filehandles and the three-argument open, open my $fh, '<', $file ...
When die-ing print the error, die "... $!";, using $! variable
The "diamond" (angle) operator, <$fh>, reads one line from a file opened with $fh when used in scalar context, as in $line = <$fh>. It advances a pointer in the file as it reads a line so the next time it's used it returns the next line. If you use it in list context then it returns all lines, but when you process a file you normally want to go line by line.
Some of the described logic and requirements aren't clear to me, but hopefully the code above is going to be easier to adjust as needed.

perl code for comparing file contents

I'm a newbie in perl scripting. I have 2 files. I want to compare contents line by line and delete the matching ones. if i use a wild card in 1 file to match multiple lines in second file, it should delete multiple matches and write the rest to another file. I got a bit from another mail it does not take care of wild cards
use strict;
use warnings;
$\="\n";
open my $FILE, "<", "file.txt" or die "Can't open file.txt: $!";
my %Set = map {$_ => undef} <$FILE>;
open my $FORBIDDEN, "<", "forbidden.txt" or die "Can't open forbidden.txt: $!";
my %Forbidden = map {$_ => undef} <$FORBIDDEN>;
open my $OUT, '>', 'output' or die $!;
my %Result = %Set; # make a copy
delete $Result{$_} for keys %Forbidden;
print $OUT keys %Result
I'm not sure what you mean with "wild card".
Nevertheless there are many ways to do what you want. Since it's prettier to use some existing modules you can use the List::Compare module available at cpan.
With the following code you use this module to store all the lines contained in the one file (file.txt) but not in the other file (forbidden.txt). So you implicitly match the lines which are equal. This code doesn't delete them from the file, but find them.
Your code would look like:
use strict;
use warnings;
use File::Slurp qw(read_file); #cpan-module
use List::Compare; #cpan-module
chomp( my #a_file = read_file 'file.txt' );
chomp( my #b_file = read_file 'forbidden.txt' );
#here it stores all the lines contained in the 'file.txt'
#but not in the 'forbidden.txt' in an array
my #a_file_only = List::Compare->new( \#a_file, \#b_file )->get_Lonly;
print "$_\n" for #a_file_only;
#here you could write these lines in a new file to store them.
#At this point I just print them out.
the new approach:
foreach my $filter (#b_file){
#a_file = grep{ /${filter}/} #a_file;
}
print Dumper(#a_file);
It will reduce the lines in the #a_file step by step by using each filter.

Read file from STDIN and write to STDOUT

I want to read html files entered from STDIN perform some function and then write another html file to STDOUT. My problem is I have to give file in the stated manner. I have tried many things but nothing is working good. Nothing is getting printed
my command line prompt
perl universe.pl<sun.html>galaxy.html
my code -
open(my $in, "<&STDIN") or die "Can't dup STDIN: $!";
open(my $out, ">&STDOUT") or die "Can't dup STDOUT: $!";
my #lines = <$in>;
foreach(#lines) {
push(#newlines,$_);
say "Lines pushing:", $_;
}
You don't need to open STDIN or STDOUT; they're always ready opened.
You don't need to slurp the whole file into memory as you do with:
my #lines = <$in>;
You never use $out which should be indicative of a problem.
while (<>)
{
print mapping_func($_);
}
where mapping_func() is your function that does the relevant transform on its input string and returns the mapped result:
sub mapping_func
{
my($string) = #_;
$string =~ s/html/HTML/gi;
return $string;
}
Using the magic diamond operator <>, you will able to do what you asked. But please to provide some more search efforts next time.
use strict; use warnings;
while (my $line = <>) {
# do something with $line
}
Last but not least; if you have to parse HTML the good way, have a look to HTML::TreeBuilder::XPath or just HTML::TreeBuilder
I had this problem with a script which was run from inside another Perl script.
The solution was to set $| in the second script (the one which executes the script which reads from STDIN).

Read newline delimited file in Perl

I am trying to read a newline-delimited file into an array in Perl. I do NOT want the newlines to be part of the array, because the elements are filenames to read later. That is, each element should be "foo" and not "foo\n". I have done this successfully in the past using the methods advocated in Stack Overflow question Read a file into an array using Perl and Newline Delimited Input.
My code is:
open(IN, "< test") or die ("Couldn't open");
#arr = <IN>;
print("$arr[0] $arr[1]")
And my file 'test' is:
a
b
c
d
e
My expected output would be:
a b
My actual output is:
a
b
I really don't see what I'm doing wrong. How do I read these files into arrays?
Here is how I generically read from files.
open (my $in, "<", "test") or die $!;
my #arr;
while (my $line = <$in>) {
chomp $line;
push #arr, $line;
}
close ($in);
chomp will remove newlines from the line read. You should also use the three-argument version of open.
Put the file path in its own variable so that it can be easily
changed.
Use the 3-argument open.
Test all opens, prints, and closes for success, and if not, print the error and the file name.
Try:
#!/usr/bin/env perl
use strict;
use warnings;
# --------------------------------------
use charnames qw( :full :short );
use English qw( -no_match_vars ); # Avoids regex performance penalty
# conditional compile DEBUGging statements
# See http://lookatperl.blogspot.ca/2013/07/a-look-at-conditional-compiling-of.html
use constant DEBUG => $ENV{DEBUG};
# --------------------------------------
# put file path in a variable so it can be easily changed
my $file = 'test';
open my $in_fh, '<', $file or die "could not open $file: $OS_ERROR\n";
chomp( my #arr = <$in_fh> );
close $in_fh or die "could not close $file: $OS_ERROR\n";
print "#arr[ 0 .. 1 ]\n";
A less verbose option is to use File::Slurp::read_file
my $array_ref = read_file 'test', chomp => 1, array_ref => 1;
if, and only if, you need to save the list of file names anyway.
Otherwise,
my $filename = 'test';
open (my $fh, "<", $filename) or die "Cannot open '$filename': $!";
while (my $next_file = <$fh>) {
chomp $next_file;
do_something($next_file);
}
close ($fh);
would save memory by not having to keep the list of files around.
Also, you might be better off using $next_file =~ s/\s+\z// rather than chomp unless your use case really requires allowing trailing whitespace in file names.

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;