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

Hi everyone,

I'm in Chapter 9 of "Learning Perl" by Schwartz and using Windows XP. Instead of updating files, they propose simply editing and completely rewriting the file. I am basically using the code as is in the book but cannot seem to get this to work at all! I have a text file "myinfo" with the following:

Program name: some junk
Author: dude
Company: no name
Phone: 123.345.1234
Version: 1.0

I would like to change the author name and remove the phone number all together.

Here's my sample code:

#!perl use strict; use 5.010; $^I = "myinfo.txt.bak"; while( <myinfo.txt> ) { s/^Author:.*/Author: Johny5/; s/^Phone:.*\n//; print; }
The book also shows a very neat short hand which I also cannot figure out how to use:

$ perl -p -i.bak -w -e 's/dude/Johnny5/g' myinfo.txt
Please help!!!

Replies are listed 'Best First'.
Re: "Updating" files.
by GrandFather (Saint) on Jan 25, 2009 at 21:08 UTC

    The "short hand" is a Perl one liner designed to be run from the command line. The -i.bak puts an in place edit loop around the code provided with -e and does the same thing as $^I = "myinfo.txt.bak"; is intended to do (but doesn't, see below for a correct version).

    The diamond operator uses a file handle or, if none is provided, opens file handles for each file name in @ARGV (generally those provided on the command line). Your code mixes the two distinct usages. Consider instead:

    use strict; use warnings; my $filename = 'myinfo.txt'; # Create the test file open my $outFile, '>', $filename or die "Failed to create $filename: $ +!"; print $outFile <<FILE; Program name: some junk Author: dude Company: no name Phone: 123.345.1234 Version: 1.0 FILE close $outFile; # 'Edit' the file local @ARGV = $filename; local $^I = '.bak'; while (<>) { next if /^Phone:/; s/^Author:.*/Author: Johny5/; print; }

    which updates myinfo.txt to contain:

    Program name: some junk Author: Johny5 Company: no name Version: 1.0

    and generates myinfo.txt.bak with the original contents of myinfo.txt.


    Perl's payment curve coincides with its learning curve.
Re: "Updating" files.
by zwon (Abbot) on Jan 25, 2009 at 20:44 UTC
    while( <myinfo.txt> )

    You can't specify filename here! You should open file first and then use handle inside <>.

    Update: or alternatively you can specify filename in command line and use just while(<>)

      I just tried the following and still no dice ...

      open ZIP, ">myinfo.txt"; $^I = "myinfo.txt.bak"; while( <> ) { s/^Author:.*/Author: Johnny5/; s/^Phone:.*\n//; print; } close ZIP;

        while(<ZIP>)

        neutron:

        Perhaps it would help if you would (a) Open the file in read mode, and (b) use your file handle!

        ...roboticus
Re: "Updating" files.
by ikegami (Patriarch) on Jan 25, 2009 at 21:09 UTC
    Close.
    #!perl use strict; use 5.010; local @ARGV = 'myinfo.txt'; local $^I = '.bak'; while( <> ) { s/^Author:.*/Author: Johny5/; s/^Phone:.*\n//; print; }

    And since you're already requiring 5.10 (for no reason), you might as well use \K

    #!perl use strict; use 5.010; local @ARGV = 'myinfo.txt'; local $^I = '.bak'; while( <> ) { s/^Author: \K.*/Johny5/; s/^Phone:.*\n//; print; }
Re: "Updating" files.
by Lawliet (Curate) on Jan 25, 2009 at 21:11 UTC
    perl -p -i.bak -w -e 's/dude/Johnny5/g' myinfo.txt

    The most confusing part of this is probably all those switches. Take a look at perlrun for help (the following instructions are directly from there).

    The -p switch assumes the following loop around your program.

    LINE: while (<>) { ... # your program goes here } continue { print or die "-p destination: $!\n"; }

    As you can see, it looks like the body of your perl program.

    The -i switch specifies that files processed by the <> construct are to be edited in-place. It does this by renaming the input file, opening the output file by the original name, and selecting that output file as the default for print() statements. The extension, if supplied, is used to modify the name of the old file to make a backup copy.

    You can see that in the one-liner, the new file's filename will be appended with '.bak'.

    The -w switch is to turn on warnings. Simple enough.

    The -e switch tells Perl to run any following code (which is between the apostrophes).

    The substitution in the actual code is simply globally replacing all instances of 'dude' with 'Johnny5'. It is more vague than your regex, and it will replace all instances, not just the first one.

    Finally, the filename at the end of the one-liner is the argument to the code.

    In other words, that one liner can be rewritten in a file as:

    #!/usr/bin/perl -w $extension = '.bak'; LINE: while (<>) { if ($ARGV ne $oldargv) { if ($extension !~ /\*/) { $backup = $ARGV . $extension; } else { ($backup = $extension) =~ s/\*/$ARGV/g; } rename($ARGV, $backup); open(ARGVOUT, ">$ARGV"); select(ARGVOUT); $oldargv = $ARGV; } s/dude/Johnny5/g; } continue { print or die "-p destination: $!\n"; }

    Pretty cool, eh.

    And you didn't even know bears could type.

      Thanks to all of you. I used the "long hand" sytle and it works like a charm.

      As for the "short hand", still can't seem to figure this one out. I've tried the command in ms-dos and in a pl file. Is it perhaps only a unix thing? If it helps to know, it does create the back-up file but gives the following error: "Useless use of a constant in void context at -e line 1."

      On that note, I feel like I'm totally missing out here. Should I be using unix to get access to perl's full potential. I know, what a newb question ...

        Try using double quotes instead of single quotes. And yeah, using Perl on unix feels more at home.

        And you didn't even know bears could type.