My Perl file generates the text file which usually contains 200 lines. Sometimes it exceeds 200 lines (For example 217 lines). I need to trim off the rest of the lines from the 201st line. I have used the counter method to trim the exceeded lines. Is there any other simple and efficient way to do this?
Code:
#!/usr/bin/perl -w
use strict;
use warnings;
my $filename1="channel.txt";
my $filename2="channel1.txt";
my $fh;
my $fh1;
my $line;
my $line1;
my $count=1;
open $fh, '<', $filename1 or die "Can't open > $filename1: $!";
open $fh1, '>', $filename2 or die "Can't open > $filename2: $!";
while(my $line = <$fh>)
{
chomp $line;
chomp $line1;
if($count<201)
{
print $fh1 "$line\n";
}
$count++;
}
close ($fh1);
close($fh);
I have already mentioned in my comment, this is short version of that comment If you actually trying to trim the file you can use the Perl One Liner instead of writing the whole code
perl -pe 'last if($. == 201);' input.text >result.txt
-p used for process the file line by line an print the output
-e execute flag, to execute the Perl syntax
With Perl script you can do this also
open my $fh,"<","input.txt";
open my $wh,">","result.txt";
print $wh scalar <$fh> for(1..10);
xxfelixxx already gave you the correct answer. I am just changing my earlier posted answer, to clean up your code and to write back to the original file:
use strict;
use warnings;
my #array;
my $filename="channel.txt";
open my $fh, '<', $filename or die "Can't open > $filename: $!";
while( my $line = <$fh> ) {
last if $. > 200;
push #array, $line;
}
close($fh);
open $fh, '>', $filename or die "Can't open > $filename: $!";
print $fh #array;
close($fh);
There is no need to keep your own counter, perl has a special variable $. which keeps track of the input line number. You can simplify your loop like so:
while( chomp( my $line = <$fh> ) ) {
last if $. > 200;
print $fh1 "$line\n";
}
perldoc perlvar - Search for INPUT_LINE_NUMBER.
To write back to the original file: input.txt without using redirection:
perl -pi.tmp -we "last if $.>200;" input.txt
where
-i : opens a temp file and automatically replaces the file to be
edited with the temporary file after processing (the '.tmp'
is the suffix to use for the temp file during processing)
-w : command line flag to 'use warnings'
-p : magic; basically equivalent to coding:
LINE: while (defined $_ = <ARGV>)) {
"your code here"
}
-e : perl code follows this flag (enclosed in double quotes for MSWin32 aficiandos)
Related
I have two text files and I want to read them by passing argument at command line.
Now how to take second file? When I give the second file name command line is not reading. Please suggest.
I have used $ARGV[0] and $ARGV[1] in the code to pass the arguments at command line.
$ ./read.pl file1 file2
Reading file1
Reading file2
$ cat read.pl
#!/usr/bin/perl
use strict;
use warnings;
readFile($_) for #ARGV;
sub readFile {
my $filename = shift;
print "Reading $filename\n";
#OPEN CLOSE stuff here
}
my ($file1, $file2) = #ARGV;
open my $fh1, '<', $file1 or die $!;
open my $fh2, '<', $file2 or die $!;
while (<$fh1>) {
do something with $_
}
while (<$fh2>) {
do something with $_
}
close $fh1;
close $fh2;
Where $_ is the default variable.
run as:
perl readingfile.pl filename1 filename2
I'm trying to remove the first line of the output file "bss_concurrent_calls.txt" using perl -pe instead of system & sed -i. The server I'm using is solaris 9 (infact it didn't recognize "sed -i")
open my $file_in, "<", "/export/home/cassi/4.1-15_HPBX/cfg/LicenseCounters.log" or die($!);
open my $file_out, '>', 'bss_concurrent_calls.txt' or die $!;
while( <$file_in> ) {
my #columns = split /\s+/, $_;
print $file_out "$columns[0]\t$columns[2]\n";
}
system('sed "1d" bss_concurrent_calls.txt');
close $file_in;
close $file_out or die $!;
No need to call sed from Perl here (or anywhere else).
perl -ane 'print "$F[0]\t$F[2]\n" unless $. == 1' \
< /export/.../LicenseCounters.log > bss_concurrent_calls.txt
I like #choroba's answer, but if you want to keep your program structure:
use autodie;
open my $file_in, '<', '/export/home/cassi/4.1-15_HPBX/cfg/LicenseCounters.log';
open my $file_out, '>', 'bss_concurrent_calls.txt';
my $line1 = <$file_in>; # read the first line
while (<$file_in>) {
print $file_out join("\t", (split)[0,2]), "\n";
}
close $file_in;
close $file_out;
while( <$file_in> ) {
next unless $i++;
my #columns = split /\s+/, $_;
print $file_out "$columns[0]\t$columns[2]\n";
}
I have a relatively simple question for you experts. I have 300 files in a directory that I want to process with my perl script (shown below). I was wondering if there is a way to use a variable and process in a batch of files in perl. I have a file containing a list of file name if this helps.
Your feedback will be appreciated.
====================================
#!/usr/bin/perl
use strict;
use warnings;
open (FILE1, "001.txt") or die ("Can't open file $!");
while(<FILE1>){
my $line = $_;
chomp $line;
if ( $line =~ m/^chr/ ) {
open OUT, '>>', '001_tmp.txt';
print OUT "$line\n";
}
}
close(OUT);
close(FILE1);
======================================
Clarification:
Basically I want the perl script that is equivalent to the following shell script where I can accommodate all files using the variable.
#!/bin/bash
if [[ $# != 1 ]]
then
echo "Usage: error <input>"
else
echo $# $1
export input=$1
grep "^chr" $1 > ${input}_tmp.vcf
So you want your while loop to read through each file in some given directory..
I would do something like this:
Use opendir and readdir so you can get the file names to operate on.
I would also look at grep to filter out the files you don't care about, in my example I filter out directories...
opendir(my $dh, $dir) or die "$dir: $!";
my #files = grep { !-d $_ } readdir $dh;
closedir $dh;
Now you will have a list of files to do work on...
for my $file (#files) {
open my $fh, "<", $file or die "$!";
while( my $line = <$fh> ) {
#TODO: stuff
}
close $fh;
}
Edit: Your tags indicated batch-file, meaning Windows batch file. If that's not what you mean, disregard this. :-)
Perhaps something like this:
From a batch file:
for /f %%x in (listoffilenames.txt) do (
perl myperlscript.pl %%x
)
And then your Perl script can be modified like this:
#!/usr/bin/perl
use strict;
use warnings;
# You may want to add a little more error handling
# around getting the filename, etc.
my $filename = shift or die "No filename specified.";
open (FILE1, "<", $filename) or die ("Can't open file $!");
while(<FILE1>){
my $line = $_;
chomp $line;
if ( $line =~ m/^chr/ ) {
open OUT, '>>', "temp-$filename";
print OUT "$line\n";
}
}
close(OUT);
close(FILE1);
I have a file that looks like this:
I,like
blah...
I want to replace only the first line with 'i,am' to get:
i,am
blah...
These are big files, so this is what I did (based on this):
open(FH, "+< input.txt") or die "FAIL!";
my $header = <FH>;
chop($header);
$header =~ s/I,like/i,am/g;
seek FH, 0, 0; # go back to start of file
printf FH $header;
close FH;
However, I get this when I run it:
i,amke
blah...
I looks like the 'ke' from like is still there. How do I get rid of it?
What I would do is probably something like this:
perl -i -pe 'if ($. == 1) { s/.*/i,am/; }' yourfile.txt
Which will only affect the first line, when the line counter for the current file handle $. is equal to 1. The regex will replace everything except newline. If you need it to match your specific line, you can include that in the if-statement:
perl -i -pe 'if ($. == 1 and /^I,like$/) { s/.*/i,am/; }' yourfile.txt
You can also look into Tie::File, which allows you to treat the file like an array, which means you can simply do $line[0] = "i,am\n". It is mentioned that there may be performance issues with this module, however.
If the replacement has a different length than the original, you cannot use this technique. You can for example create a new file and then rename it to the original name.
open my $IN, '<', 'input.txt' or die $!;
open my $OUT, '>', 'input.new' or die $!;
my $header = <$IN>;
$header =~ s/I,like/i,am/g;
print $OUT $header;
print $OUT $_ while <$IN>; # Just copy the rest.
close $IN;
close $OUT or die $!;
rename 'input.new', 'input.txt' or die $!;
I'd just use Tie::File:
#! /usr/bin/env perl
use common::sense;
use Tie::File;
sub firstline {
tie my #f, 'Tie::File', shift or die $!;
$f[0] = shift;
untie #f;
}
firstline $0, '#! ' . qx(which perl);
Usage:
$ ./example
$ head -2 example
#! /bin/perl
use common::sense;
I want to remove all lines in a text file that start with HPL_ I have acheived this and can print to screen, but when I try to write to a file, I just get the last line of the amended text printed in the new file. Any help please!
open(FILE,"<myfile.txt");
#LINES = <FILE>;
close(FILE);
open(FILE,">myfile.txt");
foreach $LINE (#LINES) {
#array = split(/\:/,$LINE);
my $file = "changed";
open OUTFILE, ">$file" or die "unable to open $file $!";
print OUTFILE $LINE unless ($array[0] eq "HPL_");
}
close(FILE);
close (OUTFILE);
exit;
You just want to remove all lines that start with HPL_? That's easy!
perl -pi -e 's/^HPL_.*//s' myfile.txt
Yes, it really is just a one-liner. :-)
If you don't want to use the one-liner, re-write the "write to file" portion as follows:
my $file = "changed";
open( my $outfh, '>', $file ) or die "Could not open file $file: $!\n";
foreach my $LINE (#LINES) {
my #array = split(/:/,$LINE);
next if $array[0] eq 'HPL_';
print $outfh $LINE;
}
close( $outfh );
Note how you are open()ing the file each time through the loop. This is causing the file to only contain the last line, as using open() with > means "overwrite what's in the file". That's the major problem with your code as it stands.
Edit: As an aside, you want to clean up your code. Use lexical filehandles as I've shown. Always add the three lines that tchrist posted at the top of every one of your Perl programs. Use the three-operator version of open(). Don't slurp the entire file into an array, as if you try to read a huge file it could cause your computer to run out of memory. Your program could be re-written as:
#!perl
use strict;
use autodie;
use warnings FATAL => "all";
my $infile = "myfile.txt";
my $outfile = "changed.txt";
open( my $infh, '<', $infile );
open( my $outfh, '>', $outfile );
while( my $line = <$infh> ) {
next if $line =~ /^HPL_/;
print $outfh $line;
}
close( $outfh );
close( $infh );
Note how with use autodie you don't need to add or die ... to the open() function, as the autodie pragma handles that for you.
The issue with your code is that you open the file for output within your line-processing loop which, due to your use of the '>' form of open, opens the file each time for write, obliterating any previous content.
Move the invocation of open() to the top of your file, above the loop, and it should work.
Also, I'm not sure of your intent but at line 4 of your example, you reopen your input file for write (using '>'), which also clobbers anything it contains.
As a side note, you might try reading up on Perl's grep() command which is designed to do exactly what you need, as in:
#!/usr/bin/perl
use strict;
use warnings;
open(my $in, '<', 'myfile.txt') or die "failed to open input for read: $!";
my #lines = <$in> or die 'no lines to read from input';
close($in);
# collect all lines that do not begin with HPL_ into #result
my #result = grep ! /^HPL_/, #lines;
open(my $out, '>', 'changed.txt') or die "failed to open output for write: $!";
print { $out } #result;
close($out);