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

I need to add the current date to file names within a document, replacing the old date. (Goal: a cron job of scripts to create fresh files for download, then update the links.)
#!/usr/bin/perl -w use Date::Format; $date=(time2str("%Y-%m-%d", time)); print "\n\n$date\n\n"; ## works 's!(\d\d\d\d-\d\d-\d\d)(-[a-z_]{5,30}\.zip)!\$date$2!g' filename.html; ## doesn't work

I have the pieces working, but not the whole. I have tried everything that I can think of (limited severely by having no noticeable programming experience and Perl experience limited to installing others' scripts).

If I need to explicitely Open the file, make the changes then Close/save it, I didn't get that far in reading (books and forums (forae?) or I've skipped it).

I know that this has been answered generally several times here and in other venues, but I haven't found a "solution" which I can understand enough to apply successfully.

Thanks,
Craig

Replies are listed 'Best First'.
Re: Regexp substitution w/ variable value (again)
by mr_mischief (Monsignor) on Apr 09, 2009 at 17:13 UTC
    It looks like you've confused what you can do from the command line with some run options and what you need to do within a larger program contained in a file. You can't just take something that would go with perl -i~ -ne as an argument on the command line and use it as a statement in a standalone program.

    You can, if you really want, use system to call perl from within a Perl program. A filter program may be in order to use with your shell's redirection. However, what you really should do if you want to make this maintainable and all in one program is learn to open your file, check for errors on the system call, read the lines, do the substitution, close the file, and check for errors on that system call, too.

    The best way to do all of that isn't to edit it in place, but to write to a new file and optionally overwrite the old file. Many people do this in the order of reading the existing file, writing the changes to a new file, then renaming the new file over the old one. I prefer to rename the old file, read the original data from the renamed file, write to a new file by the old name, then delete (if desired) the renamed file after successfully closing the new one.

    It goes a little something like this:

    #!/usr/bin/perl use strict; use warnings; use Date::Format; my $delete_temp = 0; # deleting the temp probably should be a command-line option my $old = 'filename.html'; my $tmp = $old . '.bak'; my $date = (time2str ("%Y-%m-%d", time)); print "\n\n$date\n\n"; rename $old, $tmp or die "Cannot rename $old to $tmp: $!\n"; open my $in, '<', $tmp or die "Cannot read $tmp: $!\n"; open my $out, '>', $old or die "Cannot write to $old: $!\n"; while ( <$in> ) { s!(\d\d\d\d-\d\d-\d\d)(-[a-z_]{5,30}\.zip)!$date$2!g; # no backslash before the sigil on $date print $out $_; } close $out or die "Error writing to $old: $!\n"; close $in; if ( $delete_temp ) { unlink $tmp or die "Error removing $tmp: $!\n"; }

    The backslash you have in your substitution means to replace the date with the literal string '$date' and then the contents of the variable $2. I took it out of my example and put in a comment there. What you really want is the contents of the variable, not its name.

    The deletion of the temp file, as stated in the comment for its controlling variable, would be better off as a command-line argument to your program. Some Getopt module is the usual suggested way to do that. Using those modules or rolling an argument parser is either one beyond the scope of this node.

    I'd recommend getting a good book on Perl like Effective Perl Programming, Programming Perl, or Learning Perl. Maybe you prefer one of the free books available online (or all of them, since they're free) like Beginning Perl, Learning Perl the Hard Way, or Extreme Perl.

      Thank you monks!

      Sorry 'bout the '\' with '$date' -- it was an artifact of my flailing around with quotes, slashes, and other things I thought (prayed) might help based on the error messages.

      I made the post too short but didn't want to explain to the brethren something that was "obvious." Displaying my own skill was accomplished with the question.

      Thank you for the time and thought which went into each answer. It's somewhat embarrassing to note that I have owned "Learning Perl" for about 10 years, and "Perl Cookbook" and "Advanced Perl Programming" a bit less time. I also have books on database design theory, MySQL, PHP, and Apache. And Windows Server and Linux administration as well as network wiring and a few other topics. My excuse is that I sometimes get a whole helf-hour without interruption and, as a result, have re-read the same few pages countless times.

      I find solutions or work-arounds, not having the time for properly studying a topic. (College was nearly 4 decades ago, and the PC hadn't yet been imagined. NOW I wish that i'd been (smart enough to be) in EE instead of liberal arts.)

      mr_mischief: I will try to flail less and apply your suggestions.

      Craig


      Results: Adding the (target) file name and removing my print-the-variable test and it worked perfecly! The text strings were correctly modified. No flailing required.

      - Apr 10 - fixt formatting.

Re: Regexp substitution w/ variable value (again)
by ikegami (Patriarch) on Apr 09, 2009 at 16:27 UTC

    "doesn't work" is a meaningless diagnostic. In what way does it not work? You don't even show how it's used.

    The slash before $date in the replacement expression looks hinky.

Re: Regexp substitution w/ variable value (again)
by kennethk (Abbot) on Apr 09, 2009 at 16:56 UTC

    It's generally good form to include specific examples of what strings you want to transform and what you want the results to be (see How (Not) To Ask A Question). The documentation for how to use regular expressions can be found at perlre, with a tutorial at perlretut. While it is certainly possible to do this in a one-liner, given your stated experience level, you probably want to open the file (I/O Operators), make changes and then write the info out. And, as ikegami states, I think that \$ in your regex looks hinky.

    You'll probably want something like:

    #!/usr/bin/perl use strict; use warnings; use Date::Format; my ($input_file, $output_file) = @ARGV; my $date = time2str("%Y-%m-%d", time); #print "\n\n$date\n\n"; ## works open my $input, '<', $input_file or die "Open failure on $input_file: +$!"; my @contents = (); while (defined (my $line = <$input>)) { $line =~ s/(\d\d\d\d-\d\d-\d\d)(-[a-z_]{5,30}\.zip)/$date$2/; push @contents, $line; } close $input; open my $output, '>', $output_file or die "Open failure on $output_fil +e: $!"; foreach my $line (@contents) { print $output $line; } close $output;

    This code could certainly be improved, but it should be sufficiently step-by-step to provide a good idea of how to proceed. The script as written should be called with

    > perl script.pl infile outfile

    The input and output file names can be the same for in-place edits.