rycher has asked for the wisdom of the Perl Monks concerning the following question:

Here's my predicament: I'm trying to read an html template file called 'index.txt'(basically an html page renamed to .txt). It has a value called ***password***. I have a subroutine that generates a random password. I would like another subroutine that reads in the index.txt template, replace ***password*** by calling the randomize password subroutine and assigning the random password and finally writing an 'index.html' file with all of the contents of index.txt, except that the value of ***password*** has been replaced with the randomized password.

This is what I have so far:

----------------------------- sub generatePassword { my $length = shift; my $possible = 'abcdefghijkmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUV +WXYZ'; while (length($password) < $length) { $password .= substr($possible, (int(rand(length($possible)))), 1) +; } } sub pleaseGodWork { generatePassword(8); my $readfile = "index.txt"; my $writefile = "index.html"; open (MYFILE, "$readfile") || die ("Cannot open $file: $!"); open (YOURFILE, ">$writefile") || die ("Cannot open $file: $!"); @copy_this = <MYFILE>; print YOURFILE (@copy_this); } close (MYFILE); close (YOURFILE); pleaseGodWork(); exit; -----------------------------

Replies are listed 'Best First'.
Re: Read value from file, replace, then write to new file
by doom (Deacon) on Dec 23, 2007 at 06:31 UTC

    There are multiple bugs in there. Off the top of my head, the worst ones are:

    1. You're not assigning the return from "generatePassword" to a variable, so there's no way that "pleaseGodWork" can do anything with it.
    2. You need to make the change to the appropriate line in @copy_this, e.g.
      for (@copy_this) { s{ \*\*\* password \*\*\* }{$password}xms; }
    3. the "open (MYFILE" line is missing the '<' you need to indicate read access, and while you're at it you might as well be using open in the modern style:
      open my $myfile_fh, '<', $readfile or die "Can't open $readfile +for input: $!"

    Further, it makes no sense to do those "close" operations after the end of the sub. If you're going to do them explicitly at all, put them inside the sub.

    There are a number of other stylistic objections that could be made: I would name the subs "generate_password" and "please_god_work" myself, and I'd probably slurp the whole file into one string like so: undef $/; my $myfile = <$myfile_fh>;, so then the change could be made in one step  $myfile =~ s{\*\*\* password \*\*\*}{$password}xms;

    Perhaps worst of all, you appear to be in the process of inventing your own system of html templates, and you'd really be better off using an existing one, e.g. Mason or Template::Toolkit.

      the "open (MYFILE" line is missing the '<' you need to indicate read access
      This may be considered poor style, but it's not exactly a bug. '<' is optional.
Re: Read value from file, replace, then write to new file
by GrandFather (Saint) on Dec 23, 2007 at 10:15 UTC

    You might like to consider using a a templating system such as HTML::Template. Consider:

    use strict; use warnings; use HTML::Template; # Create a sample file - for demo purposes only open OUTFILE, '>', 'sample.tmpl'; print OUTFILE <<HTML; <p>The password is: <TMPL_VAR NAME='password'></p> HTML close OUTFILE; # Here be the important sample code my $template = HTML::Template->new (filename => 'sample.tmpl'); $template->param (password => 'wibble'); print $template->output ();

    Prints:

    <p>The password is: wibble</p>

    Perl is environmentally friendly - it saves trees
Re: Read value from file, replace, then write to new file
by narainhere (Monk) on Dec 23, 2007 at 06:42 UTC
    Hie rycher,

    I don't understand why you close the file handles MYFILE and YOURFILE before you actually open them in pleaseGodWork() subroutine. It's good to close the handles in the same subroutine as you open them.

    Now you might want to use something like HTML::Parser module in-order to fix the password field in the index.txt file to replace it(In the current version there is no replacement ).


    The world is so big for any individual to conquer

Re: Read value from file, replace, then write to new file
by Cody Pendant (Prior) on Dec 23, 2007 at 09:58 UTC
    Here's a nice little sub to generate a random string, if you want one:
    sub random_string { local $" = ''; "@{[map{chr(97+int rand 26)} 1 .. shift]}"; }


    Nobody says perl looks like line-noise any more
    kids today don't know what line-noise IS ...

      local $" = ''; "@{[ ... ]}"
      instead of
      join '', ...?

      sub random_string { join '', map{chr(97+int rand 26)} 1 .. shift; }
Re: Read value from file, replace, then write to new file
by jwkrahn (Abbot) on Dec 23, 2007 at 19:20 UTC
    { my @possible = ( 'a' .. 'k', 'm', 'n', 'p' .. 'z', 2 .. 9, 'A' .. +'H', 'J' .. 'N', 'P' .. 'Z' ); sub generatePassword { my $length = shift; return join '', map $possible[ rand @possible ], 1 .. $length; } } { my $readfile = 'index.txt'; my $writefile = 'index.html'; sub pleaseGodWork { my $password = generatePassword 8; open my $IN, '<', $readfile or die "Cannot open $readfile: $!" +; open my $OUT, '>', $writefile or die "Cannot open $writefile: +$!"; select $OUT; s/\Q***password***/$password/g, print while <$IN>; } } pleaseGodWork; exit 0;
Re: Read value from file, replace, then write to new file
by Cop (Initiate) on Dec 23, 2007 at 19:37 UTC

    So the generated password is recorded in a file as it is. What kind of shit is this? So anyone take a look at the file will know the password.

    If this is a real system, nobody, including you or the system admin is supposed to know this.

      Thanks everyone for the help. I was half-expecting to be brutally made fun of and laughed at for the sloppy coding. ;-)
      I will try some of these sugesstions.
      Cop: The randomly generated password is going to change every day. The file gets overwritten once a day with a new password. This password will reside in a password-protected web area for the admins of that particular to view and assign to users.