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

I'm having problems with an in place edit on a file. I'm hoping someone can shed some light on a solution. The file I'm attempting to in-line edit has this sort of format:-
nfs 1 active (followed by a variable number of words) tempfs 2 active (followed by a variable number of words) ufs 0 locked (followed by a variable number of words) . . and so on
I'm changing the second field only like this :-
local @ARGV = ("diskmon"); local $^I = '.bak'; while (<>) { m/^(\w+)\s+(\d+)$/; printf "$1 %d %s\n", exists $disk{$1} ? 0 : $2+$partition; }
This is giving me :-
nfs 1 tmpfs 6 ufs 2
Whilst the second field has been changed as required, I've lost the rest of the record. I know I'm doing something dumb ??

Replies are listed 'Best First'.
Re: In place edit and printf
by japhy (Canon) on Dec 09, 2005 at 16:23 UTC
    I don't see how your code could work at all. Your regex won't match any of the lines you've shown, so $1 and $2 are going to be empty. You've got TWO format items in your printf(), but you're only supplying ONE item to that format list. And you've got a precedence problem with the ?: operator and addition. Try this:
    while (<>) { if (/^(\w+)\s+(\d+)(.*)/) { printf "%s %d %s\n", $1, (exists $disk{$1} ? 0 : ($2+$partition)), + $3; } else { print } }

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: In place edit and printf
by TedYoung (Deacon) on Dec 09, 2005 at 16:21 UTC

    You are not giving printf a value for the %s token. Either pass in $' as the next arg, or replace %s in the picture with $'. Also, this may have been a retype error, but you probably don't want the $ at the end of your regex since there is more text to follow the digits.

    while (<>) { m/^(\w+)\s+(\d+)(.*)$/ or die; # You should # always check a regex before # using $1, etc. printf "%s %d %s", $1, (exists $disk{$1} ? 0 : $2+$partition), $3 +; }

    Updated: Removed $1 from the picture per suggestion below.

    Ted Young

    ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)

      Your solution has two problems:

      1) Injection Vulnerability

      You don't escape the %s in $1 and $'. Pass them as arguments to printf instead, adding %s in the format string where necessary

      2) Performance Leak

      Using $' can cause other (unrelated) regexp in your program to slow down. Add (.*) to the end of the regexp and use $2 instead of $'.

        You would think with all the recent noise about the printf bug I would have remembered that. I was too focused on the initial question at hand.

        Ted Young

        ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
      Thank you for the answer. I've taken note of the performance and vulnerability issues mentioned.