Increment variables in files - perl

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;

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.

How to make an array containing strings in a file separated by space?

I have a file perl_script_2_out_2.txt and I want to put all strings separated by space in an array #arr.
I wrote this code but isnt working .
open my $FILE4, '<', 'perl_script_2_out_2.txt' or die $!;
my #array4 = <FILE4>;
close($FILE4);
open my $FILE5, '>', 'perl_script_2_out_2.txt' or die $!;
foreach $_ (#array4) {
s/\s+/\n/g;
print $FILE5 "$_";
}
close($FILE5);
open my $FILE6, '<', 'perl_script_2_out_2.txt' or die $!;
#arr = <$FILE6>;
You must always use strict and use warnings at the top of every Perl program that you write. In this case you would have seen the message
Name "main::FILE4" used only once: possible typo
which points to the statement
my #array4 = <FILE4>
and helps you to see that you have opened the file handle $FILE4 but tried to read from FILE4, which is different.
If you fix that then your code will work, but it's a strange way to do things and it's much better like this. I have used Data::Dump to display the final contents of the array; it's not necessary for the program to work.
use strict;
use warnings;
open my $fh, '<', 'perl_script_2_out_2.txt' or die $!;
my #arr;
while (<$fh>) {
push #arr, split;
}
use Data::Dump;
dd \#arr;
output
[
"uart_1_baddress",
2211,
"uart_2_baddress",
3344,
"uart_3_baddress",
2572,
]

File not getting copied in 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.

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";
}

How can I insert a line at the beginning of a file with Perl's Tie::File?

I'm trying to insert/add a line 'COMMENT DUMMY' at the beginnig of a file as a first row if /PATTERN/ not found.
I know how to do this with OPEN CLOSE function. Probably after reading the file it should look something like this:
open F, ">", $fn or die "could not open file: $!"; ;
print F "COMMENT DUMMY\n", #array;
close F;
But I have a need to implement this with the use of the Tie::File function and don't know how.
use strict;
use warnings;
use Tie::File;
my $fn = 'test.txt';
tie my #lines, 'Tie::File', $fn or die "could not tie file: $!";
untie #lines;
unshift works:
use Tie::File;
my $fn = 'test.txt';
tie my #lines, 'Tie::File', $fn or die "could not tie file: $!";
unshift #lines, "COMMENT DUMMY\n";
untie #lines;
Kinopiko's pointed you in the right direction. To complete your need, I'd do the following:
use strict;
use warnings;
use Tie::File;
my $fileName = 'test.txt';
tie my #lines, 'Tie::File', $fileName or die "Unable to tie $fileName: $!";
unshift #lines, "DUMMY COMMENT\n" if grep { /PATTERN/ } #lines;
untie #lines;
Explanation
You may already know that although the if statement comes after the unshift statement in writing, it gets evaluated first.
When you see grep, think of it as a list filter. Basically, it takes your #lines list and uses it to create a new list with just elements that match /PATTERN/.
The if statement evaluates to true if the new, filtered list contains any elements, and false if the list is empty. Based on this, the "DUMMY COMMENT\n" line is added to your #lines list.
The point of tie is to make one thing behave like another. Since you are tie-ing a file to an array, it now acts like an array. You use array operators to do whatever you need to do.