Unwanted double-interpolation in string passed to backticks

Discussion in 'Perl Misc' started by Henry Law, May 28, 2004.

  1. Henry Law

    Henry Law Guest

    I think I've done my homework on this, including "How can I call
    backticks without shell processing?" in perlfaq8, and a Groups search
    on "perl shell escape characters backticks" but I'm none the wiser;
    pointers would be welcome.

    Here's a test program:
    -----------------------
    #! /usr/bin/perl

    use strict;
    use warnings;

    print "Enter filename:";
    my $filename = <STDIN>;
    chomp $filename;

    my $ret = `ls -m $filename`;

    print "Returned value:$ret\n";
    -----------------------

    The problem comes when the file name that is entered contains the "$"
    character, which in my case it often does. In the test directory
    there is a file called "test" and another called "$test". If I run
    the program above and enter "test" the result is as expected, thus:

    $ ./test
    Enter filename:test
    Returned value:test

    But if I enter the name of the "$test" file, the variable $filename is
    interpolated a second time, and since there is no "$test" variable
    within the program it comes out as null, thus:

    $ ./test
    Enter filename:$test
    Returned value:bashref.html, c, d, (etc.. the whole directory)

    Escaping the $ within the backticks won't work, since I do want Perl
    to interpolate $filename, so what to do? My current work-round is a
    sub called "shell_execute" which takes the string to be executed
    (after one level of interpolation) and then escapes the dollar signs
    before running backticks. It works, but ITNABWTDI?

    Henry Law <>< Manchester, England
     
    Henry Law, May 28, 2004
    #1
    1. Advertising

  2. Henry Law

    Anno Siegel Guest

    Henry Law <> wrote in comp.lang.perl.misc:
    > I think I've done my homework on this, including "How can I call
    > backticks without shell processing?" in perlfaq8, and a Groups search
    > on "perl shell escape characters backticks" but I'm none the wiser;
    > pointers would be welcome.
    >
    > Here's a test program:
    > -----------------------
    > #! /usr/bin/perl
    >
    > use strict;
    > use warnings;
    >
    > print "Enter filename:";
    > my $filename = <STDIN>;
    > chomp $filename;
    >
    > my $ret = `ls -m $filename`;
    >
    > print "Returned value:$ret\n";
    > -----------------------
    >
    > The problem comes when the file name that is entered contains the "$"
    > character, which in my case it often does. In the test directory
    > there is a file called "test" and another called "$test". If I run
    > the program above and enter "test" the result is as expected, thus:
    >
    > $ ./test
    > Enter filename:test
    > Returned value:test
    >
    > But if I enter the name of the "$test" file, the variable $filename is
    > interpolated a second time, and since there is no "$test" variable
    > within the program it comes out as null, thus:
    >
    > $ ./test
    > Enter filename:$test
    > Returned value:bashref.html, c, d, (etc.. the whole directory)


    The unwanted interpolation happens in the shell that is called to
    execute "ls". To avoid this, escape the dollar:

    $filename =~ s'\$'\\$'g;

    Anno
     
    Anno Siegel, May 28, 2004
    #2
    1. Advertising

  3. Henry Law

    Paul Lalli Guest

    On Fri, 28 May 2004, Henry Law wrote:

    > Here's a test program:
    > -----------------------
    > #! /usr/bin/perl
    >
    > use strict;
    > use warnings;
    >
    > print "Enter filename:";
    > my $filename = <STDIN>;
    > chomp $filename;
    >
    > my $ret = `ls -m $filename`;
    >
    > print "Returned value:$ret\n";
    > -----------------------
    >
    > The problem comes when the file name that is entered contains the "$"
    > character, which in my case it often does. In the test directory
    > there is a file called "test" and another called "$test". If I run
    > the program above and enter "test" the result is as expected, thus:
    >
    > $ ./test
    > Enter filename:test
    > Returned value:test
    >
    > But if I enter the name of the "$test" file, the variable $filename is
    > interpolated a second time, and since there is no "$test" variable
    > within the program it comes out as null, thus:
    >
    > $ ./test
    > Enter filename:$test
    > Returned value:bashref.html, c, d, (etc.. the whole directory)


    Perl is not interpolating your variable a second time. The shell is
    interpolating the variable $test. To verify, try entering the command in
    your shell, without perl:
    ls -m $test

    You will see the same results, that is, an entire directory listing. In
    the shell, you'd have to do:
    ls -m \$test

    which should give you the clue of how to solve this. You could search and
    replace all 'special' characters to have a backslash precede them.
    However, Perl gives you a way to do this:

    $return = `ls -m \Q$filename\E`;
    or
    $filename = quotemeta $filename
    $return = `ls -m $filename`;

    Read about quotemeta in perldoc -f quotemeta, and \Q in perldoc perlop
    under "Quote and Quote-like Operators"

    Paul Lalli
     
    Paul Lalli, May 28, 2004
    #3
  4. Henry Law

    Anno Siegel Guest

    Paul Lalli <> wrote in comp.lang.perl.misc:
    > On Fri, 28 May 2004, Henry Law wrote:
    >
    > > Here's a test program:
    > > -----------------------
    > > #! /usr/bin/perl
    > >
    > > use strict;
    > > use warnings;
    > >
    > > print "Enter filename:";
    > > my $filename = <STDIN>;
    > > chomp $filename;
    > >
    > > my $ret = `ls -m $filename`;
    > >
    > > print "Returned value:$ret\n";
    > > -----------------------


    [...]

    > However, Perl gives you a way to do this:
    >
    > $return = `ls -m \Q$filename\E`;
    > or
    > $filename = quotemeta $filename
    > $return = `ls -m $filename`;
    >
    > Read about quotemeta in perldoc -f quotemeta, and \Q in perldoc perlop
    > under "Quote and Quote-like Operators"


    With quotemeta() you'll also escape "/", which is unwanted in file
    names.

    Anno
     
    Anno Siegel, May 28, 2004
    #4
  5. Henry Law

    Paul Lalli Guest

    On Fri, 28 May 2004, Anno Siegel wrote:

    > > However, Perl gives you a way to do this:
    > >
    > > $return = `ls -m \Q$filename\E`;
    > > or
    > > $filename = quotemeta $filename
    > > $return = `ls -m $filename`;
    > >
    > > Read about quotemeta in perldoc -f quotemeta, and \Q in perldoc perlop
    > > under "Quote and Quote-like Operators"

    >
    > With quotemeta() you'll also escape "/", which is unwanted in file
    > names.


    Unneeded, perhaps. But it has no ill effect, at least not with the shell
    I'm using (I believe it's bash).

    ls -al foo\/bar
    has the same effect as
    ls -al foo/bar

    Are there shells out there that would throw an error at this?

    Paul Lalli
     
    Paul Lalli, May 28, 2004
    #5
  6. Henry Law

    Ben Morrow Guest

    Quoth "Henry Law" <>:
    > I think I've done my homework on this, including "How can I call
    > backticks without shell processing?" in perlfaq8, and a Groups search
    > on "perl shell escape characters backticks" but I'm none the wiser;
    > pointers would be welcome.
    >
    > Here's a test program:
    > -----------------------
    > #! /usr/bin/perl
    >
    > use strict;
    > use warnings;
    >
    > print "Enter filename:";
    > my $filename = <STDIN>;
    > chomp $filename;
    >
    > my $ret = `ls -m $filename`;
    >
    > print "Returned value:$ret\n";
    > -----------------------
    >
    > The problem comes when the file name that is entered contains the "$"
    > character, which in my case it often does. In the test directory
    > there is a file called "test" and another called "$test". If I run
    > the program above and enter "test" the result is as expected, thus:
    >
    > $ ./test
    > Enter filename:test
    > Returned value:test
    >
    > But if I enter the name of the "$test" file, the variable $filename is
    > interpolated a second time,


    By the shell, not Perl.

    > and since there is no "$test" variable
    > within the program it comes out as null, thus:


    You say you've read the faq answer; why didn't you try it?

    my $ret = do {
    open my $LS, '-|', ls => -m => $filename
    or die "can't fork ls: $!";
    local $/;
    <$LS>;
    };

    As a separate issue, is possible to define some sort of DESTROY method
    to call die automatically if the implicit close at end of scope fails?
    It would make this sort of code both safe and clean.

    Ben

    --
    If you put all the prophets, | You'd have so much more reason
    Mystics and saints | Than ever was born
    In one room together, | Out of all of the conflicts of time.
    The Levellers, 'Believers'
     
    Ben Morrow, May 28, 2004
    #6
  7. Henry Law

    Henry Law Guest

    On Fri, 28 May 2004 14:51:21 +0000 (UTC), Ben Morrow
    <> wrote:

    >You say you've read the faq answer; why didn't you try it?


    I did; and of course it worked. But forking sounded like too much
    heavy-duty workload for something so trivial, and which I have to do
    many times within this particular program, so I ended up with my
    current solution, which is the sub which excapes $'s and then executes
    the command within backticks.

    Henry Law <>< Manchester, England
     
    Henry Law, May 28, 2004
    #7
  8. Henry Law

    Ben Morrow Guest

    Quoth "Henry Law" <>:
    > On Fri, 28 May 2004 14:51:21 +0000 (UTC), Ben Morrow
    > <> wrote:
    >
    > >You say you've read the faq answer; why didn't you try it?

    >
    > I did; and of course it worked. But forking sounded like too much
    > heavy-duty workload for something so trivial, and which I have to do
    > many times within this particular program, so I ended up with my
    > current solution, which is the sub which excapes $'s and then executes
    > the command within backticks.


    Backticks perform a fork. If you want to execute an external command,
    you *have* to fork.

    In fact, the open '-|' answer is lighter, as backticks will fork twice:
    once for the shell and again for ls. Avoiding the shell will remove a
    completely extraneous process.

    Always benchmark before deciding something is 'too heavy-duty'.

    Ben

    --
    Musica Dei donum optimi, trahit homines, trahit deos. |
    Musica truces molit animos, tristesque mentes erigit. |
    Musica vel ipsas arbores et horridas movet feras. |
     
    Ben Morrow, May 28, 2004
    #8
  9. Henry Law

    Guest

    "Henry Law" <> wrote:
    > On Fri, 28 May 2004 14:51:21 +0000 (UTC), Ben Morrow
    > <> wrote:
    >
    > >You say you've read the faq answer; why didn't you try it?

    >
    > I did; and of course it worked. But forking sounded like too much
    > heavy-duty workload for something so trivial,


    Do you have any idea how much work qx{} does behind the scenes?

    > and which I have to do
    > many times within this particular program, so I ended up with my
    > current solution, which is the sub which excapes $'s and then executes
    > the command within backticks.


    Why not use glob? That is probably lighter (or at least as light) than any
    shell-based method, and much easier to figure out the escaping for.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
     
    , May 28, 2004
    #9
  10. Henry Law

    Anno Siegel Guest

    Paul Lalli <> wrote in comp.lang.perl.misc:
    > On Fri, 28 May 2004, Anno Siegel wrote:
    >
    > > > However, Perl gives you a way to do this:
    > > >
    > > > $return = `ls -m \Q$filename\E`;
    > > > or
    > > > $filename = quotemeta $filename
    > > > $return = `ls -m $filename`;
    > > >
    > > > Read about quotemeta in perldoc -f quotemeta, and \Q in perldoc perlop
    > > > under "Quote and Quote-like Operators"

    > >
    > > With quotemeta() you'll also escape "/", which is unwanted in file
    > > names.

    >
    > Unneeded, perhaps. But it has no ill effect, at least not with the shell
    > I'm using (I believe it's bash).
    >
    > ls -al foo\/bar
    > has the same effect as
    > ls -al foo/bar
    >
    > Are there shells out there that would throw an error at this?


    I don't know, but the fact that the question even arises makes a case
    against using quotemeta to quote strings for a shell. It may work,
    but it's not the right tool for the task.

    Anno
     
    Anno Siegel, May 28, 2004
    #10
  11. Henry Law

    Anno Siegel Guest

    Ben Morrow <> wrote in comp.lang.perl.misc:

    [...]

    > my $ret = do {
    > open my $LS, '-|', ls => -m => $filename
    > or die "can't fork ls: $!";
    > local $/;
    > <$LS>;
    > };
    >
    > As a separate issue, is possible to define some sort of DESTROY method
    > to call die automatically if the implicit close at end of scope fails?
    > It would make this sort of code both safe and clean.


    You can bless the globref that is the filehandle into a class, so yes.
    DESTROY is called (immediately) before the handle is closed, so you'd
    have to close it yourself to warn on errors.

    There is, of course, no good way to give the user the name under which
    the handle was originally opened. That could be handled if the blessing
    happened at open() time, when the file name is known.

    Another problem with this approach turns up when the filehandle already is
    a blessed object.

    Anno
     
    Anno Siegel, May 28, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dee
    Replies:
    9
    Views:
    515
    Joseph Byrns
    Apr 15, 2005
  2. Sydex
    Replies:
    12
    Views:
    6,508
    Victor Bazarov
    Feb 17, 2005
  3. Simon Bunker

    single/double quote escape interpolation

    Simon Bunker, Jul 7, 2003, in forum: Python
    Replies:
    2
    Views:
    665
    Bengt Richter
    Jul 8, 2003
  4. Anand
    Replies:
    2
    Views:
    906
    Anand
    Sep 11, 2003
  5. ccc31807

    double variable interpolation question

    ccc31807, May 22, 2009, in forum: Perl Misc
    Replies:
    8
    Views:
    104
    Mart van de Wege
    May 22, 2009
Loading...

Share This Page