problems with replacing first line of file using perl - perl

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;

Related

To trim lines based on line number in perl

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)

Perl find and replace one liner

I've been looking at the other find and replace questions on Perl, and I'm not sure how exactly to implement this variation in one line. The only difference is that I want to replace two things, one is the original, and one is the replacing a modification of the original string. The code I have in a pm module is:
my $num_args = $#ARGV + 1;
if ($num_args != 2) {
print "\nUsage: pscript.pm path to file [VERSION]\n";
exit;
}
my $version = $ARGV[1];
my $revision = $ARGV[1];
$revision =~ s/.*\.//g;
open ($inHandle, "<", $ARGV[0]) or die $^E;
open ($outHandle, ">", "$ARGV[0].mod") or die $^E;
while(my $line = <$inHandle>)
{
$line =~ s/\<Foo\>(.*)\<\/Foo\>/\<Foo\>$version\<\/Foo\>/;
$line =~ s/\<Bar\>(.*)\<\/Bar\>/\<Bar\>$revision\<\/Bar\>/;
print $outHandle $line;
}
close $inHandle;
close $outHandle;
unlink $ARGV[0];
rename "$ARGV[0].mod", $ARGV[0];
What is different is:
$revision =~ s/.*\.//g;
which turns the version X.X.X.1000 into just 1000, and then uses that for the find and replace.
Can this be done using the
perl -i.bak -p -e 's/old/new/g;' *.config
format?
Try this :
perl -i.bak -pe 's/\d+\.\d+\.\d+\.1000\b/1000/g' *.config

Extract file contents between given lines using perl

I want to use only Sed in Perl to capture the file contents between 1000 and 2000 lines in a given file.
I tried the below but it didn't work,Can someone help me on this please.
$firstLIne="1000";
$lastline="2000";
$output=`sed -n '$firstLIne,$lastline'p sample.txt`;
Here is another pure perl solution:
my ($firstline, $lastline) = (1000,2000);
open my $fh, '<', 'sample.txt' or die "$!";
while(<$fh>){
print if $. == $firstline .. $. == $lastline;
}
if you don't use the variables anywhere else, you can use the special use case of .. with constants (4th paragraph if you use constant expression they automatically get compared to $.):
while(<$fh>){
print if 1000 .. 2000;
}
Here is the important part from the perldoc for the .. operator:
In scalar context, ".." returns a boolean value. The operator is bistable, like a flip-flop, and emulates the line-range (comma) operator of sed, awk, and various editors.
Edit Per request, with storing the intermediate lines in a variable.
my ($firstline, $lastline) = (1000,2000);
my $output = '';
open my $fh, '<', 'sample.txt' or die $!;
while(<$fh>){
$output .= $_ if $. == $firstline .. $. == $lastline;
}
print $ouput;
Also, if your file isn't too big (it fits completely into memory) you also can read it into a list and select the lines you're interested in:
my $output = join '', (<$fh>)[$firstline+1..$lastline]
For comparison, to do this in Perl only, one could write:
my $firstLine=1000;
my $lastLine=2000;
my $fn="sample.txt";
my $output;
open (my $fh, "<", $fn) or die "Could not open file '$fn': $!\n";
while (<$fh>) {
last if $. > $lastLine;
$output .= $_ if $. >= $firstLine;
}
close($fh);
Note that this will stop reading from file after line $lastLine.. so if the file contains 100,000 lines it will only read the first 2000 lines..
If you just want to print out the lines then:
perl -ne 'print if 1000 .. 2000' example_data.txt
should work.
If you want to incorporate that into a script somehow then you can "semi-slurp" the filehandle:
use strict;
use warnings;
open my $filehandle, 'example_data.txt' or die $!;
my $lines_1k_to_2k ;
while (<$filehandle>) {
$lines_1k_to_2k .= $_ if 1000 .. 2000 ;
}
print $lines_1k_to_2k ;
The .= operator will add the lines to the string in variable $lines_1k_to_2k only if they are in the range 1000 .. 2000

Remove first line of a file using perl -pe

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

How to read specific lines from file and store in an array using perl?

How can i read/store uncommented lines from file into an array ?
file.txt looks like below
request abcd uniquename "zxsder,azxdfgt"
request abcd uniquename1 "nbgfdcbv.bbhgfrtyujk"
request abcd uniquename2 "nbcvdferr,nscdfertrgr"
#request abcd uniquename3 "kdgetgsvs,jdgdvnhur"
#request abcd uniquename4 "hvgsfeyeuee,bccafaderryrun"
#request abcd uniquename5 "bccsfeueiew,bdvdfacxsfeyeueiei"
Now i have to read/store the uncommented lines (first 3 lines in this script) into an array. is it possible to use it by pattern matching with string name or any regex ? if so, how can i do this ?
This below code stores all the lines into an array.
open (F, "test.txt") || die "Could not open test.txt: $!\n";
#test = <F>;
close F;
print #test;
how can i do it for only uncommented lines ?
If you know your comments will contain # at the beginning you can use
next if $_ =~ m/^#/
Or use whatever variable you have to read each line instead of $_
This matches # signs at the beginning of the line.
As far as adding the others to an array you can use push (#arr, $_)
#!/usr/bin/perl
# Should always include these
use strict;
use warnings;
my #lines; # Hold the lines you want
open (my $file, '<', 'test.txt') or die $!; # Open the file for reading
while (my $line = <$file>)
{
next if $line =~ m/^#/; # Look at each line and if if isn't a comment
push (#lines, $line); # we will add it to the array.
}
close $file;
foreach (#lines) # Print the values that we got
{
print "$_\n";
}
You could do:
push #ary,$_ unless /^#/;END{print join "\n",#ary}'
This skips any line that begins with #. Otherwise the line is added to an array for later use.
The smallest change to your original program would probably be:
open (F, "test.txt") || die "Could not open test.txt: $!\n";
#test = grep { $_ !~ /^#/ } <F>;
close F;
print #test;
But I'd highly recommend rewriting that slightly to use current best practices.
# Safety net
use strict;
use warnings;
# Lexical filehandle, three-arg open
open (my $fh, '<', 'test.txt') || die "Could not open test.txt: $!\n";
# Declare #test.
# Don't explicitly close filehandle (closed automatically as $fh goes out of scope)
my #test = grep { $_ !~ /^#/ } <$fh>;
print #test;