Perl replace string (newstring oldstring) - perl

Implement a program that processes an input file by changing every occurrence of an old string into a new string. (The usage is: chstr file oldstring newstring, chstr is your program name, file, oldstring and newstring are parameters specified by user.)
if( #ARGV < 2)
{
print "usage: ReplaceString.pl filename OldString NewString\n";
print " example: perl ReplaceString.pl intelliquest.txt ";
print "IntelliQuest Kantar > kantar.txt\n";
exit 0;
}
$OldString = $ARGV[1];
$NewString = $ARGV[2];
open(MYFILE,$ARGV[0]) || die "Cannot open file \"$ARGV[0]\"";
while($line = <MYFILE>)
{
$line =~ s/$OldString/$NewString/g;
print STDOUT $line;
}
really not sure what is wrong here, I try and run
jd#jd-laptop:~/Desktop$ perl HW1-2.pl text.txt if the
To replace if with the and i get...
syntax error at HW1-2.pl line 11, near "<"
syntax error at HW1-2.pl line 11, near "&gt"
syntax error at HW1-2.pl line 15, near "}"
Execution of HW1-2.pl aborted due to compilation errors.
Do i need the &lt and &gt? I'm really new to Perl
Thanks in advance

Whatever tutorial you based this on was clearly written by someone who can't be bothered to check his work. < and > are supposed to be < and >, respectively, but somewhere along the line it got overly HTML-encoded.
Specifically, the line
while($line = <MYFILE>)
should be changed to:
while($line = <MYFILE>)

jwodder has the answer, but you seem confused by it:
This works on my Mac. If you are running this on a Linux, Mac, or Unix system, you need that
first line I have #! /usr/bin/env perl. Or else, you need to run your program as perl ReplaceString.pl from the command line.
One Windows, you have to make sure that the .pl suffix is mapped to your Perl interpreter.
#! /usr/bin/env perl
if( #ARGV < 2) {
print "usage: ReplaceString.pl filename OldString NewString\n";
print " example: perl ReplaceString.pl intelliquest.txt ";
print "IntelliQuest Kantar > kantar.txt\n";
exit 0;
}
$OldString = $ARGV[1];
$NewString = $ARGV[2];
open(MYFILE,$ARGV[0]) || die "Cannot open file \"$ARGV[0]\"";
while( $line = <MYFILE> ) {
chomp $line;
$line =~ s/$OldString/$NewString/g;
print "$line\n";
}
I take it your taking lessons and learning Perl.
I'm just curious where you're learning Perl. This is syntax I would expect to see back in the old Perl 3.x days. Perl has advanced quite a bit since then, and I would think your teacher would help you with the newer style of syntax.

Related

End of file in readline

I'm trying to get an Perl program working. I get an error
readline() on closed filehandle IN at Test.pl line 368, <IN> line 65.
Lines 363-369 of the program looks like this
print "Primer3 is done! \n";
my $forward = "$snpid.for";
my #forward_out;
my $i=0;
open(IN,$forward);
while(<IN>){
chomp;s/\r//;
And refers to the configuration file. The last line (line 65) looks like this
num_cpus = 40
So the configuration file is not correct, or Perl does not recognize that this is the end of the file.
Is there a way to solve this?
Update
Based on the comments I added the open() or die command and got this:
No such file or directory at Test.pl line 367.
The open command is part of a subroutine Primer3_Run
sub Primer3_Run {
my $snpid = shift;
my $seq = shift;
my $tmp_input = "Primer3.tmp.input";
my $len = length($seq);
open(OUT, ">$tmp_input");
close OUT;
if ( -e "$snpid.for" ) {
system "del
$snpid.for";
}
if ( -e "$snpid.rev" ) {
system "del $snpid.rev";
}
system
"$params{'primer3'}
Primer3.tmp.input
Primer3.Log.txt 2>&1 ";
my $forward = "$snpid.for";
my #forward_out;
my $i = 0;
open(IN, $forward) or die $!;
From what you've revealed, the problem will be this block
if ( -e "$snpid.for" ) { system "del $snpid.for"; }
followed soon afterwards by this
my $forward = "$snpid.for";
open(IN, $forward) or die $!;
The new or die $! is presumably from my advice, so previously you were deleting "$snpid.for" and then trying to open it for input. The error you saw should be expected
No such file or directory at Test.pl line 367.
You just deleted it!
All I can think of that may help is to be more organised with your coding. What did you mean when you tried to open a file that you had just made sure didn't exist?
Beyond that, you must always add use strict and use warnings 'all' at the top of every Perl program you write. Use lexical file handles together with the three-parameter form of open and always check the status of every open call, using die including the value of $! in the die string to explain why the open failed.

Perl - prepend/remove line numbers to a file

I'm currently writing a Perl script that needs to have an option within a menu that will prepend line numbers to a file and another option to remove those line numbers.
example outputs: "This is the first line of my file" should be changed to "01 - This is the first line of my file"
and
"01 - This is the first line of my file" should be changed to "This is the first line of my file"
I've thought of using loops to accomplish this but I have a feeling that there might be a more simple solution. How should I go about solving this?
From the command line:
To add the line numbers as requested:
perl -ne 'printf("%02d - ",$.);print' < file > newfile
To remove the line numbers:
perl -ne 's/^\d* - //;print' < newfile
Update
To edit the file "in place" use the -i flag. Also -p instead of -n and print. From the command line:
To add the line numbers as requested:
perl -pe 'printf("%02d - ",$.)' -i file
To remove the line numbers:
perl -pe 's/^\d* - //' -i file
Since you want to use it in a script I am going to extend on nirys answer:
I don't think there is going to be a more simple and mainainable solution than using a loop. One example with foreach:
sub addLinenumbersToFile {
my($file) = #_;
open my $in_handle, "<", $file;
my #lines = <$in_handle>;
close $in_handle;
my $line_count = 1;
foreach my $line (#lines) {
# modify element directly:
$line = sprintf("%02d - ", $line_count) . $line;
$line_count++;
}
open my $out_handle, ">", $file;
print $out_handle join('', #lines);
}
You can then call this function with the filename as parameter: addLinenumbersToFile("inputfile.txt");
And to return the file to it's original state:
sub removeLinenumbersFromFile {
my($file) = #_;
open my $in_handle, "<", $file;
my #lines = <$in_handle>;
close $in_handle;
my $line_count = 1;
foreach my $line (#lines) {
my $formatted_line_count = sprintf("%02d - ", $line_count);
# modify element directly:
$line =~ s/^$formatted_line_count//;
$line_count++;
}
open my $out_handle, ">", $file;
print $out_handle join('', #lines);
}
Since the line number remover uses the exact same format the chances of modifying the wrong file by accident are not very big. They could be further reduced by leaving the function after one line didn't match.

Perl reading from a file and writing to another using print and inderect handler

I have a file called malwareip.txt with a list as IP :
1.1.1.1
2.2.2.2
I need to read from this file and create another file (query.txt) so that the final results be:
ip.dst=1.1.1.1 || ip.dst=2.2.2.2
I have created the following script. .However I see a || in the first line as under:
||ip.dst=1.1.1.1
||ip.dst=2.2.2.2
Why I'm getting a || before the ip.dst=1.1.1.1?
See my script below. Thanks.
#!/usr/bin/env perl
use strict;
use warnings;
my $filename="malwareip.txt";
open (my $ip, "<" , $filename) || die ("Can't open file malwareip.txt");
my $outputfile="query.txt";
open (my $out, ">" , $outputfile) || die ("CAN'T OPEN FILE query.txt");
my $OR="||";
while ( <$ip> ) {
next if (/^$/);
printf $out "ip.dst=$_$OR";
}
close $out;
close $ip;
Your current output does not make sense, because you cannot get the || at the start of the output unless you print it there. Not even if you happen to have blank lines in your file, because it would still print ip.dst= before that blank line. So, you must be mistaken about getting that output, or about having that code.
Because you forgot to chomp your input, you would normally get output like this
ip.dst=1.1.1.1
||ip.dst=2.2.2.2
||
If you have non-standard line endings, such as using a file with CR \r, then all your lines would get overwritten, but you would get only one line of output: The last one.
||ip.dst=2.2.2.2
So your output makes no sense, and it cannot be explained until you supply more information.
If I were to do something like this, I would do:
perl -lwe 'chomp(#a = <>); print join "||", grep /\S/, #a;' malwareip.txt > query.txt
You're missing chomp.
You're printing || every time your print, even though you want one less.
You might have CRLF line endings on a system which expects LF line endings. s/\s+\z// will chomp off both LF and CRLF line endings.
my $OR = '';
while ( <$ip> ) {
s/\s+\z//;
next if /^$/;
print $out "${OR}ip.dst=$_";
$OR = ' || ';
}
print $out "\n";
Note the use of print rather than printf. printf expects its first argument to be a format parameter.
print $out "${OR}ip.dst=$_"; # ok
printf $out "%s", "${OR}ip.dst=$_"; # ok
printf $out "%sip.dst=%s", $OR, $_; # ok
printf $out "${OR}ip.dst=$_"; # Not really ok

how to output the second line in a multi-line file [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have a big file with repeated lines as follows:
#UUSM
ABCDEADARFA
+------qqq
!2wqeqs6777
I will like to output the all the 'second line' in the file. I have the following code snipped for doing this, but it's not working as expected. Lines 1, 3 and 4 are in the output instead.
open(IN,"<", "file1.txt") || die "cannot open input file:$!";
while (<IN>) {
$line = $line . $_;
if ($line =~ /^\#/) {
<IN>;
#next;
my $line = $line;
}
}
print "$line";
Please help!
try this
open(IN,"<", "file1.txt") || die "cannot open input file:$!";
my $lines = "";
while (<IN>) {
if ($. % 4 == 2) $lines .= $_;
}
print "$lines";
I assume what you are asking is how to print the line that comes after a line that begins with #:
perl -ne 'if (/^\#/) { print scalar <> }' file1.txt
This says, "If the line begins with #, then print the next line. Do this for all the files in the argument list." The scalar function is used here to impose a scalar context on the file handle, so that it does not print the whole file. By default print has a list context for its arguments.
If you actually want to print the second line in the file, well, that's even easier. Here's a few examples:
Using the line number $. variable, printing if it equals line number 2.
perl -ne '$. == 2 and print, close ARGV' yourfile.txt
Note that if you have multiple files, you must close the ARGV file handle to reset the counter $.. Note also the use of the lower precedence operator and will force print and close to both be bound to the conditional.
Using regular logic.
perl -ne 'print scalar <>; close ARGV;'
perl -pe '$_ = <>; close ARGV;'
Both of these uses a short-circuit feature by closing the ARGV file handle when the second line is printed. If you should want to print every other line of a file, both these will do that if you remove the close statements.
perl -ne '$at = $. if /^\#/; print if $. - 1 == $at' file1.txt
Written out longhand, the above is equivalent to
open my $fh, "<", "file1.txt";
my $at_line = 0;
while (<$fh>) {
if (/^\#/) {
$at_line = $.;
}
else {
print if $. - 1 == $at_line;
}
}
If you want lines 2, 6, 10 printed, then:
while (<>)
{
print if $. % 4 == 2;
}
Where $. is the current line number — and I didn't spend the time opening and closing the file. That might be:
{
my $file = "file1.txt";
open my $in, "<", $file or die "cannot open input file $file: $!";
while (<$in>)
{
print if $. % 4 == 2;
}
}
This uses the modern preferred form of file handle (a lexical file handle), and the braces around the construct mean the file handle is closed automatically. The name of the file that couldn't be opened is included in the error message; the or operator is used so the precedence is correct (the parentheses and || in the original were fine too and could be used here, but conventionally are not).
If you want the line after a line starting with # printed, you have to organize things differently.
my $print_next = 0;
while (<>)
{
if ($print_next)
{
print $_;
$print_next = 0;
}
elsif (m/^#/)
{
$print_next = 1;
}
}
Dissecting the code in the question
The original version of the code in the question was (line numbers added for convenience):
1 open(IN,"<", "file1.txt") || die "cannot open input file:$!";
2 while (<IN>) {
3 $line = $line . $_;
4 if ($line =~ /^\#/) {
5 <IN>;
6 #next;
7 my $line = $line;
8 }
9 }
10 print "$line";
Discussion of each line:
OK, though it doesn't use a lexical file handle or report which file could not be opened.
OK.
Premature and misguided. This adds the current line to the variable $line before any analysis is done. If it was desirable, it could be written $line .= $_;
Suggests that the correct description for the desired output is not 'the second lines' but 'the line after a line starting with #. Note that since there is no multi-line modifier on the regex, this will always match only the first line segment in the variable $line. Because of the premature concatenation, it will match on each line (because the first line of data starts with #), executing the code in lines 5-8.
Reads another line into $_. It doesn't test for EOF, but that's harmless.
Comment line; no significance except to suggest some confusion.
my $line = $line; is a self-assignment to a new variable hiding the outer $line...mainly, this is weird and to a lesser extent it is a no-op. You are not using use strict; and use warnings; because you would have warnings if you did. Perl experts use use strict; and use warnings; to make sure they haven't made silly mistakes; novices should use them for the same reason.
Of itself, OK. However, the code in the condition has not really done very much. It skips the second line in the file; it will later skip the fourth, the sixth, the eighth, etc.
OK.
OK, but...if you're only interested in printing the lines after the line starting #, or only interested in printing the line numbers 2N+2 for integral N, then there is no need to build up the entire string in memory before printing each line. It will be simpler to print each line that needs printing as it is found.

Read the last line of file with data in Perl

I have a text file to parse in Perl. I parse it from the start of file and get the data that is needed.
After all that is done I want to read the last line in the file with data. The problem is that the last two lines are blank. So how do I get the last line that holds any data?
If the file is relatively short, just read on from where you finished getting the data, keeping the last non-blank line:
use autodie ':io';
open(my $fh, '<', 'file_to_read.txt');
# get the data that is needed, then:
my $last_non_blank_line;
while (my $line = readline $fh) {
# choose one of the following two lines, depending what you meant
if ( $line =~ /\S/ ) { $last_non_blank_line = $line } # line isn't all whitespace
# if ( line !~ /^$/ ) { $last_non_blank_line = $line } # line has no characters before the newline
}
If the file is longer, or you may have passed the last non-blank line in your initial data gathering step, reopen it and read from the end:
my $backwards = File::ReadBackwards->new( 'file_to_read.txt' );
my $last_non_blank_line;
do {
$last_non_blank_line = $backwards->readline;
} until ! defined $last_non_blank_line || $last_non_blank_line =~ /\S/;
perl -e 'while (<>) { if ($_) {$last = $_;} } print $last;' < my_file.txt
You can use the module File::ReadBackwards in the following way:
use File::ReadBackwards ;
$bw = File::ReadBackwards->new('filepath') or
die "can't read file";
while( defined( $log_line = $bw->readline ) ) {
print $log_line ;
exit 0;
}
If they're blank, just check $log_line for a match with \n;
If the file is small, I would store it in an array and read from the end. If its large, use File::ReadBackwards module.
Here's my variant of command line perl solution:
perl -ne 'END {print $last} $last= $_ if /\S/' file.txt
No one mentioned Path::Tiny. If the file size is relativity small you can do this:
use Path::Tiny;
my $file = path($file_name);
my ($last_line) = $file->lines({count => -1});
CPAN page.
Just remember for the large file, just as #ysth said it's better to use File::ReadBackwards. The difference can be substantial.
sometimes it is more comfortable for me to run shell commands from perl code. so I'd prefer following code to resolve the case:
$result=`tail -n 1 /path/file`;