For Programmers: Free Programming Magazines  


Home > Archive > PERL Beginners > November 2006 > Problem with replacing string in file









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Problem with replacing string in file
Perl Pra

2006-11-25, 7:57 am

hi Gurus,

I have a problem to replace strings of file thru perl script.


Here is the problem in detail...


I have a text file some thing like this..


PROJ_FOLER=C:\Proj
PROJ_LOGS=C:\PROJ\LOGS


I have same line in config file some thing like this.


PROJ_FOLDER=D:\Proj
PROJ_LOGS=D:\PROJ\LOGS.


Now i read variables from text file split them like this

my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;

and place both in a varaible $rep_line="$key=$val";

and search in the config file with $key (which has PROJ_FOLDER) and get to
the result to $line.

Now i will replace $line with $rep_line using

perl -i -p -e 's/$line/$rep_line/g' $file;

Here is the code i have written...

BEGIN________

#!/usr/bin/perl
my $config_path="E:/MessageArchive/WorkArea/config.txt";
$file="E:/temp/FT/config/FTMessageArchive.configd";
open FH, "$config_path";

while ($line=<FH> )
{


my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;
$repline="$key=$val";
open $LOGFILE, '<', $file;
while ($line1 = <$LOGFILE> )
{
if ($line1 =~ m/$key/){ system("perl -i.bak -p -e
's/$line1/$repline/g' $file");close $LOGFILE;last;}

}
}
close(FH);
END _____________

if i run the script I am getting the following errror..


Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.

What am i doing wrong?

Please help me on this..


Thanks in Advance

PP..

boyd

2006-11-25, 7:57 am

In article
< 8070ef410611250440h77c537c0k8c5d36e5fa10
fcab@mail.gmail.com>,
perlpra@gmail.com (Perl Pra) wrote:
....
>
> #!/usr/bin/perl
> my $config_path="E:/MessageArchive/WorkArea/config.txt";
> $file="E:/temp/FT/config/FTMessageArchive.configd";
> open FH, "$config_path";
>
> while ($line=<FH> )
> {
>
>
> my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;
> $repline="$key=$val";
> open $LOGFILE, '<', $file;
> while ($line1 = <$LOGFILE> )
> {
> if ($line1 =~ m/$key/){ system("perl -i.bak -p -e
> 's/$line1/$repline/g' $file");close $LOGFILE;last;}
>
> }
> }
> close(FH);
> END _____________

....

Interesting script. I don't think I would have thought up this way to
solve the problem. I think the error may be caused by the $repline
variable - it probably has metacharacters in it that have special
meaning to reg. exp., so they should be "escaped" with "\";

I will try writing a short script "my way" and post it soon.

Boyd
FishMonger

2006-11-25, 3:44 pm

quote:
Originally posted by Perl Pra
hi Gurus,

I have a problem to replace strings of file thru perl script.


Here is the problem in detail...


I have a text file some thing like this..


PROJ_FOLER=C:\Proj
PROJ_LOGS=C:\PROJ\LOGS


I have same line in config file some thing like this.


PROJ_FOLDER=D:\Proj
PROJ_LOGS=D:\PROJ\LOGS.


Now i read variables from text file split them like this

my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;

and place both in a varaible $rep_line="$key=$val";

and search in the config file with $key (which has PROJ_FOLDER) and get to
the result to $line.

Now i will replace $line with $rep_line using

perl -i -p -e 's/$line/$rep_line/g' $file;

Here is the code i have written...

BEGIN________

#!/usr/bin/perl
my $config_path="E:/MessageArchive/WorkArea/config.txt";
$file="E:/temp/FT/config/FTMessageArchive.configd";
open FH, "$config_path";

while ($line=<FH> )
{


my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;
$repline="$key=$val";
open $LOGFILE, '<', $file;
while ($line1 = <$LOGFILE> )
{
if ($line1 =~ m/$key/){ system("perl -i.bak -p -e
's/$line1/$repline/g' $file");close $LOGFILE;last;}

}
}
close(FH);
END _____________

if i run the script I am getting the following errror..


Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Can't find string terminator "'" anywhere before EOF at -e line 1.

What am i doing wrong?

Please help me on this..


Thanks in Advance

PP..



That's an odd and inefficient approach. You should look into using a module suited for this purpose.

Here are a few option.
http://search.cpan.org/author/MJD/T...lib/Tie/File.pm
http://search.cpan.org/author/CANGE.../File/AsHash.pm
http://search.cpan.org/search?query=ini&mode=all
FishMonger

2006-11-25, 3:49 pm

http://search.cpan.org/author/CANGE.../File/AsHash.pm
D. Bolliger

2006-11-25, 9:58 pm

perl pra am Samstag, 25. November 2006 13:40:
> hi Gurus,
>
> I have a problem to replace strings of file thru perl script.

[...]
> I have a text file some thing like this..
> PROJ_FOLER=C:\Proj
> PROJ_LOGS=C:\PROJ\LOGS
>
> I have same line in config file some thing like this.
> PROJ_FOLDER=D:\Proj
> PROJ_LOGS=D:\PROJ\LOGS.

[...]
> Here is the code i have written...


Hi perl pra

I did not test your code, and won't present a solution, because I think it is
more helpful to make you think about what you coded, and how you can simplify
coding, and then you will find the solution yourself :-)

> #!/usr/bin/perl


Never forget:

use strict;
use warnings;

you have to declare all variables now, and will see warnings and hints about
possible error sources.

> my $config_path="E:/MessageArchive/WorkArea/config.txt";
> $file="E:/temp/FT/config/FTMessageArchive.configd";
> open FH, "$config_path";


Replace the couble qoutes with single qoutes in the first two lines, no
variable is interpolated; in the third, a variable without any static text is
unnecessarily interpolated, so you can omit quoting at all.

Note the usage of FH and $LOGFILE as file handles. In newer style, use a
variable:

open my $fh, '<', $config_path or die $!;

> while ($line=<FH> ) {
> my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;


The test if the matching succeeded is missing. Your data may be proper
formatted or not. If not, $key and $val will be undefined, leading to
unexpected results in the following code. Never assume properly formatted
input data.

You only read one line, so the /m modifier is useless.

A data line, I assume, should only have one 'X=Y', so the /g modifier is a bit
strange. You may want to handle unexpected data lines in some way.

> $repline="$key=$val";


You break $line into parts and then put it together in $repline again. I don't
see at the moment what's the sense behind it?!

> open $LOGFILE, '<', $file;


*Always* check:

open $LOGFILE, '<', $file or die $!; # even better: more verbose msg

> while ($line1 = <$LOGFILE> ) {
> if ($line1 =~ m/$key/){
> system("perl -i.bak -p -e 's/$line1/$repline/g' $file");


*Always* check. system returns 0 on success.

Then, the error messages you get, indicate that the code given to system
contains some errors. What to do? Simply print out it to see what you pass to
system. Eventually run the code directly in the shell.

You deal with user provided input here that is passed to the shell. So be
*very* cautious about what is executed, and consider malicious input data.

Also note that you are in a while loop stepping through $file, and you try to
modify $file via system. Generally it's a bad idea to alter something you're
looping through.

> close $LOGFILE;


*Always* check.

> last;
> }
> }
> }
>
> close(FH);


*Always check.

> END _____________
>
> if i run the script I am getting the following errror..
>
>
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.

[...]
> What am i doing wrong?


The most important point I think is that you should check as much as possible
and assume as less as possible :-)

Then, as a next step, you may consider a redesign. Consider (poor pseudocode):

while ...{
while ...{
system...
}
}

I don't know how many lines your files contain, but system could be executed
numerous times, every time creating a new process!



Dani (nonguru)
boyd

2006-11-25, 9:58 pm

In article <tbmoore9-CAC060.08374625112006@news.verizon.net>,
boyd <tbmoore9@verizon.net> wrote:

> In article
> < 8070ef410611250440h77c537c0k8c5d36e5fa10
fcab@mail.gmail.com>,
> perlpra@gmail.com (Perl Pra) wrote:
> ...
>

Here's my version (not completely - I tried to retain as much of yours
as I could :) ) It is longer than it has to be, but it is easier to
follow without shortcuts.

Boyd

#!/usr/bin/perl
use strict;
use warnings;
use Carp;
$|++;

my $config_path = "conf.txt";
my $file = "log.txt";
my %config_hash = ();
open FH, "$config_path";
while ( my $line = <FH> ) {
chomp $line; # get rid of eol stuff
my ( $key, $val ) = $line =~ /^(\w+)=(.+)$/mg;
$config_hash{$key} = $val;
}
close FH;

open my $LOGFILE, '<', $file;
my @log_lines = <$LOGFILE>; #slurp in the file - good for small files
close $LOGFILE;
my $changed = 0; # flag to see if we change the array
for my $line (@log_lines) {
chomp $line;
my ( $key, $val ) = $line =~ /^(\w+)=(.+)$/mg;
if ( exists $config_hash{$key} ) {
$line = "$key=$config_hash{$key}";
$changed++; # yes, we changed it.
}
}

if ($changed) {
#0 == system("copy $file $file.bak") # the MS-DOS version (I think)
0 == system("cp $file $file.bak") # the unix version
or die "Could not make backup of $file\n";
open $LOGFILE, '>', $file;
for my $line (@log_lines) {
print $LOGFILE "$line\n";
}
close $LOGFILE;
}
nobull67@gmail.com

2006-11-25, 9:58 pm

boyd wrote:
>
> Interesting script. I don't think I would have thought up this way to
> solve the problem.


What, you don't enter obfuscatd code competitions?

nobull67@gmail.com

2006-11-25, 9:58 pm


boyd wrote:
> In article <tbmoore9-CAC060.08374625112006@news.verizon.net>,
> boyd <tbmoore9@verizon.net> wrote:
>
> Here's my version (not completely - I tried to retain as much of yours
> as I could :) ) It is longer than it has to be, but it is easier to
> follow without shortcuts.
>
> Boyd
>
> #!/usr/bin/perl
> use strict;
> use warnings;
> use Carp;


You don't actually use Carp so why use Carp?

> $|++;


You never write anything to STDOUT so what is the point putting it into
autoflush mode?

> my $config_path = "conf.txt";
> my $file = "log.txt";
> my %config_hash = ();


The explicit initializaion is noise, newly declared hashes start out
empty.

> open FH, "$config_path";


You forgot "or die $!".

> while ( my $line = <FH> ) {
> chomp $line; # get rid of eol stuff
> my ( $key, $val ) = $line =~ /^(\w+)=(.+)$/mg;


Remove the /mg it is just noise. As is the $ for that matter.

Always check success. If you want to assume the match always succedes
then just append "or die".

> $config_hash{$key} = $val;
> }
> close FH;
>
> open my $LOGFILE, '<', $file;


You forgot "or die $!".

> my @log_lines = <$LOGFILE>; #slurp in the file - good for small files
> close $LOGFILE;
> my $changed = 0; # flag to see if we change the array


Since it's just a boolean, undef will do just fine as "false" - no need
to set it to 0.

> for my $line (@log_lines) {
> chomp $line;
> my ( $key, $val ) = $line =~ /^(\w+)=(.+)$/mg;


Again the /mg is just noise.

> if ( exists $config_hash{$key} ) {


Again you should check the match succeded. Just checking defined($key)
would do.

> $line = "$key=$config_hash{$key}";
> $changed++; # yes, we changed it.
> }
> }
>
> if ($changed) {
> #0 == system("copy $file $file.bak") # the MS-DOS version (I think)
> 0 == system("cp $file $file.bak") # the unix version
> or die "Could not make backup of $file\n";


Probably better to rename rather than copy. Which means you can use the
Perl bultin rename().

> open $LOGFILE, '>', $file;


You forgot "or die $!".

> for my $line (@log_lines) {
> print $LOGFILE "$line\n";
> }
> close $LOGFILE;
> }


Mumia W.

2006-11-25, 9:58 pm

On 11/25/2006 06:40 AM, perl pra wrote:
> hi Gurus,
>
> I have a problem to replace strings of file thru perl script.
>
>
> Here is the problem in detail...
>
>
> I have a text file some thing like this..
>
>
> PROJ_FOLER=C:\Proj
> PROJ_LOGS=C:\PROJ\LOGS
>
>
> I have same line in config file some thing like this.
>
>
> PROJ_FOLDER=D:\Proj
> PROJ_LOGS=D:\PROJ\LOGS.
>
>
> Now i read variables from text file split them like this
>
> my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;
>
> and place both in a varaible $rep_line="$key=$val";
>
> and search in the config file with $key (which has PROJ_FOLDER) and get to
> the result to $line.
>
> Now i will replace $line with $rep_line using
>
> perl -i -p -e 's/$line/$rep_line/g' $file;
>
> Here is the code i have written...
>
> BEGIN________
>
> #!/usr/bin/perl


Missing:
use strict;
use warnings;

Modify your program to work with them.

> my $config_path="E:/MessageArchive/WorkArea/config.txt";
> $file="E:/temp/FT/config/FTMessageArchive.configd";
> open FH, "$config_path";
>
> while ($line=<FH> )
> {
>
>
> my ($key,$val)= $line =~ /^(\w+)=(.+)$/mg ;
> $repline="$key=$val";
> open $LOGFILE, '<', $file;
> while ($line1 = <$LOGFILE> )
> {
> if ($line1 =~ m/$key/){ system("perl -i.bak -p -e
> 's/$line1/$repline/g' $file");close $LOGFILE;last;}
>


Inline editing, on my platform, results in the original file being
renamed and eventually deleted. Why are you creating a separate perl
process to modify a file you already have open?


> }
> }
> close(FH);
> END _____________
>
> if i run the script I am getting the following errror..
>
>
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
> Can't find string terminator "'" anywhere before EOF at -e line 1.
>
> What am i doing wrong?
>


My eyes are too old to see a stray ' somewhere in a program. My guess is
that it has something to do with the call to system (which shouldn't be
called anyway).

> Please help me on this..
>
>
> Thanks in Advance
>
> PP..
>


HTH

boyd

2006-11-25, 9:58 pm

In article <1164467751.150423.55210@l12g2000cwl.googlegroups.com>,
"nobull67@gmail.com" <nobull67@gmail.com> wrote:

nobull: Thanks for the critique. I tried to just patch the previous
script with as few changes as possible.

I use the header lines recommended by Perl Best Practices, so they are
on a mapped key in my .gvimrc. There are different teaching styles - my
way works with some students, not others...

Boyd
Rob Dixon

2006-11-25, 9:58 pm

Mumia W. wrote:
>
> On 11/25/2006 06:40 AM, perl pra wrote:
[snip][color=darkred]
[snip][color=darkred]
>
> My eyes are too old to see a stray ' somewhere in a program. My guess is
> that it has something to do with the call to system (which shouldn't be
> called anyway).
>


It's for two reasons. Firstly $line1 isn't chomped, so there is a newline before
the second slash in the substitution; and secondly Windows doesn't recognise
single quotes as being special on a command line, so Perl is being passed a
parameter list like:

-i.bak
-p
-e
's/PROJ_FOLDER=D:\Proj
/PROJ_FOLDER=D:\Proj/g'
E:/temp/FT/config/FTMessageArchive.configd

and clearly the one-liner has no string terminator!

Rob

FishMonger

2006-11-26, 11:27 am

quote:
Originally posted by FishMonger
http://search.cpan.org/author/CANGE.../File/AsHash.pm


Here's an example using one of the modules I recommend which is a cleaner and IMO better approach.
code:
use warnings; use strict; use Tie::File::AsHash; my $file1 = "E:/MessageArchive/WorkArea/config.txt"; my $file2 = "E:/temp/FT/config/FTMessageArchive.configd"; tie my %file1, 'Tie::File::AsHash', $file1, split => '=' or die "Problem tying %file1: $!"; tie my %file2, 'Tie::File::AsHash', $file2, split => '=' or die "Problem tying %file2: $!"; foreach my $key (keys %file1) { $file1{$key} = $file2{$key} if exists $file2{$key}; } untie %file1; untie %file2;
Perl Pra

2006-11-29, 7:58 am

thanks rob and Samstag..
have done it

thanks a lot.


On 11/25/06, Rob Dixon <rob.dixon@350.com> wrote:
>
> Mumia W. wrote:
> [snip]
> [snip]
>
> It's for two reasons. Firstly $line1 isn't chomped, so there is a newline
> before
> the second slash in the substitution; and secondly Windows doesn't
> recognise
> single quotes as being special on a command line, so Perl is being passed
> a
> parameter list like:
>
> -i.bak
> -p
> -e
> 's/PROJ_FOLDER=D:\Proj
> /PROJ_FOLDER=D:\Proj/g'
> E:/temp/FT/config/FTMessageArchive.configd
>
> and clearly the one-liner has no string terminator!
>
> Rob
>
>
> --
> To unsubscribe, e-mail: beginners-unsubscribe@perl.org
> For additional commands, e-mail: beginners-help@perl.org
> <http://learn.perl.org/> <http://learn.perl.org/first-response>
>
>
>


Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com