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

I am attempting to allow the user to invoke a script and supply a substitution expression on the command-line. I then want to apply the substitution to a list of values using eval.

I would expect the following code to work:

my $op = shift; for (qw(your hand is too cold to hold)) { eval $op; print $_, "\n"; }

If the user invokes the script as follows:

perl test.pl 's/old/new/'

The output is:

your hand is too cold to hold

I would expect the "old"s to be "new"s, and I'm sure I'm making a dumb mistake.

Replies are listed 'Best First'.
Re: Using eval to s/// with a pattern supplied on the command-line
by blokhead (Monsignor) on Mar 16, 2005 at 21:49 UTC
    You don't check whether the eval succeeds or not. In fact, it is failing, as you can see by printing $@:
    my $op = shift; for (qw/your hand is too cold to hold/) { eval $op; print $@, $_, $/; } __OUTPUT__ Modification of a read-only value attempted at (eval 1) line 1. your Modification of a read-only value attempted at (eval 2) line 1. hand Modification of a read-only value attempted at (eval 3) line 1. is Modification of a read-only value attempted at (eval 4) line 1. too Modification of a read-only value attempted at (eval 5) line 1. cold Modification of a read-only value attempted at (eval 6) line 1. to Modification of a read-only value attempted at (eval 7) line 1. hold
    One way to fix it is to change the for loop to for my $x (...) and add local $_ = $x at the top of the loop.

    blokhead

      Well, I was right, forgetting to check $@ is a pretty dumb mistake.

      Thanks for the catch.

      Yes, but this code
      for (qw(your hand is too cold to hold)) { s/cold/new/; print $_, "\n"; }
      should run and DWIM. I have no idea why it doesn't. I get Modification of a read-only value attempted at C:\t.pl line 3.


      holli, /regexed monk/
        This is because the qw-list is a literal, thus not modifyable.
        holli,
        Would you expect "cold" =~ s/cold/new/; to work? Remember that the looping variable in a Perl style for loop is an alias to that which is being looped over.

        Cheers - L~R

Re: Using eval to s/// with a pattern supplied on the command-line
by ikegami (Patriarch) on Mar 16, 2005 at 21:55 UTC

    Checking if the eval succeeded finds the cause I suspected:

    use strict; use warnings; my $op = shift; for (qw(your hand is too cold to hold)) { eval $op; print $@ if $@; print $_, "\n"; } __END__ Modification of a read-only value attempted at (eval 1) line 1. your Modification of a read-only value attempted at (eval 2) line 1. hand Modification of a read-only value attempted at (eval 3) line 1. is Modification of a read-only value attempted at (eval 4) line 1. too Modification of a read-only value attempted at (eval 5) line 1. cold Modification of a read-only value attempted at (eval 6) line 1. to Modification of a read-only value attempted at (eval 7) line 1. hold

    Fix:

    use strict; use warnings; my $op = shift; for (qw(your hand is too cold to hold)) { local $_ = $_; eval $op; print $_, "\n"; } __END__ your hand is too cnew to hnew
Re: Using eval to s/// with a pattern supplied on the command-line
by Roy Johnson (Monsignor) on Mar 16, 2005 at 23:05 UTC
    Don't you think you should have the user supply a pattern and a replacement as two separate arguments, and then plug them into s/// yourself (which would not require an eval)? What you're really doing is allowing the user to insert any code at all.

    Caution: Contents may have been coded under pressure.

      Actually, if you want to allow backreferences, it will still require an eval. And, really, security is one thing for random users doing nasty things (especially, but not particularly limited to, CGI), but sometimes you just want the power of perl (and its regexp engine) made trivially simple at the commandline. I think this is one of the latter cases. ;-)

        You're right. The utility I've been asked to provide is not intended to be used by anyone other than the requester, who wants the flexibility, and is aware of the risk, but not concerned.

        Thanks to all of you who responded!