Re: search and replace with a variable
by davido (Cardinal) on Apr 09, 2014 at 15:27 UTC
|
I think what you're talking about is an in-place edit. Here is why in-place edits are difficult; file systems don't facilitate growing or shrinking files from the middle. It's almost always just at the end where a file size is allowed to change. So if you want to replace 'bar' with 'bart' somewhere other than the end of the file, you have a problem.
The simplest way to solve this problem is by avoiding it. And one good way to avoid it is to open your input file, open an output file, and copy the data from the input file to the output file, while making the change. When done, move the output file to replace the input file.
So a general framework might look like this:
open my $in, '<', 'infile.txt' or die $!;
open my $out, '>', 'outfile.txt' or die $!;
while( my $line = <$in> ) {
$line =~ s/example/replacement/;
print $out $line;
}
close $in;
close $out or die $!;
rename 'outfile.txt', 'infile.txt' or die $!;
You've got two additional issues worth mentioning; First, you seem to be dealing with CSV, so you should be working with Text::CSV, Text::CSV_XS, or DBD::CSV with DBI. Don't solve your problem some fragile homebrewed way when there's a well-tested CSV tool (several) available.
Second, you seem to be working within a CGI environment. That means your script could be invoked several times at once from several web requests. That means you need to deal with flock to prevent race conditions. This set of slides from Mark Jason Dominus is a helpful introduction: File Locking Tricks and Traps.
| [reply] [d/l] |
Re: search and replace with a variable
by mtmcc (Hermit) on Apr 09, 2014 at 15:20 UTC
|
What is the content of @lines? My guess is the problem lies there. You shouldn't need the /e modifier.
This works, for example:
#!/usr/bin/perl
use warnings;
use strict;
my $fqdn = 'first';
my $address = 'second';
my @lines = ('RECORDNAME', 'RECORDIP', 'third');
print STDERR "BEFORE: @lines\n";
foreach(@lines)
{
$_ =~ s/RECORDNAME/$fqdn/;
$_ =~ s/RECORDIP/$address/;
}
print STDERR "AFTER: @lines\n";
| [reply] [d/l] |
Re: search and replace with a variable
by ww (Archbishop) on Apr 09, 2014 at 15:26 UTC
|
I have this nagging, uncomfortable sense that I've not understood your question... but does this (simplified and not cargo-cult-able) code do what you intend?
#!/usr/bin/perl
use strict;
use warnings;
use 5.016;
my @lines = ("RECORDNAME foo bar baz RECORDIP 127.2.2.1 \n", "RECORDNA
+ME 1 2 3 4 5 RECORDIP 127.1.1.1\n", "RECORDNAME abcd e fgh ij RECORDI
+P 127.0.0.1\n");
my $fqdn = qq/www.test.sample/;
my $address = qq/nowhere_ville/;
say @lines;
say "-" x10 . "\n";
my @newlines = '';
for $_(@lines) {
$_ =~ s/RECORDNAME/$fqdn/; #no need to /ee if your vars are sca
+lars
$_ =~ s/RECORDIP/$address/;
push(@newlines, $_);
}
say @newlines;
OUTPUT:
> perl 1081659.pl
RECORDNAME foo bar baz RECORDIP 127.2.2.1
RECORDNAME 1 2 3 4 5 RECORDIP 127.1.1.1
RECORDNAME abcd e fgh ij RECORDIP 127.0.0.1
----------
www.test.sample foo bar baz nowhere_ville 127.2.2.1
www.test.sample 1 2 3 4 5 nowhere_ville 127.1.1.1
www.test.sample abcd e fgh ij nowhere_ville 127.0.0.1
If not, try re-reading your node and clarify (if you can infer it) whatever confused me. And read about quotelike operators ( perlop - scroll down to "Regexp Quote-Like Operators" and "Quote-Like Operators" ) for possibly useful information.
Questions containing the words "doesn't work" (or their moral equivalent) will usually get a downvote from me unless accompanied by:
- code
- verbatim error and/or warning messages
- a coherent explanation of what "doesn't work actually means.
| [reply] [d/l] [select] |
|
|
Here's the entire code. Those values are being read in via CGI. See below. Note that defining the variables with those same values work just fine.
#!/usr/bin/perl
use strict;
use CGI;
use Getopt::Long;
use FindBin qw($Bin $Script);
my ($BASE,$NAME)=($Bin,$Script) ;
my ( $fqdn, $address );
my ( @results, $result, $response );
my $sestimeout = 3600;
open(FILE, "<ADDACS.csv") || die "File not found";
my @lines = <FILE>;
close(FILE);
my $q = CGI->new;
my $fqdn = $q->param('fqdn');
chomp $fqdn;
my $address = $q->param('address');
chomp $address;
my @newlines;
foreach(@lines) {
$_ =~ s/RECORDNAME/$fqdn1/;
$_ =~ s/RECORDIP/$address/;
push(@newlines,$_);
}
open(FILE, ">ADDACS1.csv") || die "File not found";
#print "Content-type: text/plain\n\n";
print FILE @newlines;
close(FILE);
print "Content-type: text/plain\n\n";
print "$address / $fqdn";
| [reply] [d/l] |
|
|
Your CGI application probably runs as some other user and group id's (not your userid). The user is dependent on the web server configuration -- possibly "nobody", or "www-...". Does the user or group that the webserver runs as have permission to write to files?
More importantly, the code you posted won't compile because it uses strict, but never declares $fqdn1, which is the variable your substitution operator is interpolating as the substitute. You should be seeing "Global symbol "$fqdn1" requires explicit package name at XXXXXX.cgi line ...". That may show up in your server log. The server log may have other helpful information, but at minimum, the code you posted will produce that error. If the code you posted is different from the code you're running, go give yourself 30 lashes, repent, and don't do it again.
My guess is that your actual script just doesn't have "use strict" at the top, and you added it here so we wouldn't jump on you. Because the bug you're describing, the fact that the substitution isn't replacing the match with anything, is exactly what would happen if you try to substitute $fqdn1 (which is undeclared and undefined), instead of $fqdn, which holds a parameter value. That means we're being lied to when you say "Here's the entire code." If that were the entire code, and the exact code, you would have a different set of symptoms from what you reported.
Furthermore, the code you posted could be refined a bit, and still needs to implement flocking so that you avoid race conditions that can occur when two web hits come in within close time proximity to each other. Here is an untested example:
use strict;
use warnings;
use CGI;
use Getopt::Long;
use FindBin;
use Fcntl q(:flock);
use constant FILENAME => "FindBin::Bin/file.csv";
open my $self_fh, "< $0" or die "Cannot open self: $!";
flock $self_fh, LOCK_EX or die "Cannot obtain an exclusive lock: $!";
open my $infh, '<', FILENAME or die "Unable to open input file: $!";
open my $outfh, '>', FILENAME . '.out' or die "Unable to open output f
+ile: $!";
my $q = CGI->new;
chomp ( my $fqdn = $q->param('fqdn') );
chomp ( my $address = $q->param('address') );
while( <$infh> ) {
s/RECORDNAME/$fqdn/;
s/RECORDIP/$address/;
print $outfh $_;
}
close $infh;
close $outfh or die "Failed to close output file: $!";
rename FILENAME . '.out', FILENAME
or die "Unable to replace original file: $!";
close $self_fh or die $!; # Releases the lock.
print "Content-type: text/plain\n\n";
print "$address / $fqdn";
The type of locking here is "blocking". In your finished product you might instead want to use a non-blocking flock, and if a lock wasn't obtained, sleep for one second and try again. Repeat five times, and if still unsuccessful, report a useful error message and exit.
| [reply] [d/l] [select] |
|
|
If you value your system, data and sanity, when you're using CGI, use -T (taint) as well. (Yes, there are exceptions, but since you're still asking us to believe in mystery source_data, this isn't one of them!)
Questions containing the words "doesn't work" (or their moral equivalent) will usually get a downvote from me unless accompanied by:
- code
- verbatim error and/or warning messages
- a coherent explanation of what "doesn't work actually means.
check Ln42!
| [reply] [d/l] |
Re: search and replace with a variable
by NetWallah (Canon) on Apr 09, 2014 at 15:26 UTC
|
| [reply] |
|
|
...but it doesn't. The replacements just end up being blanks. Do note that it works just fine when I declare those variable as direct strings i.e.
my $fqdn = 'XXXX_NAME_vpn';
my $address = '143.55.67.89';
| [reply] [d/l] |
|
|
Well, that makes it pretty clear: your problem is in the code you didn't show us; the part by which you obtain and generate the scalars! (possibly need to chomp?)
check Ln42
Questions containing the words "doesn't work" (or their moral equivalent) will usually get a downvote from me unless accompanied by:
- code
- verbatim error and/or warning messages
- a coherent explanation of what "doesn't work actually means.
| [reply] |
|
|
|
|
|
|
|
Re: search and replace with a variable
by vinoth.ree (Monsignor) on Apr 09, 2014 at 15:10 UTC
|
Hitronmason,
To help you better, please show us the values of $fqdn and $address variables.
| [reply] [d/l] [select] |
|
|
$fqdn = 10.55.140.136 and $address = XXXX_Charlotte_VPN
| [reply] |