Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

comment on

( #3333=superdoc: print w/replies, xml ) Need Help??

A small meditation started by Ssh and qx


Intro

Let's face it: qx is evil, as soon as you want to reliably pass arguments to a program. And it's not necessarily perl's fault. Blame the default shell (Update: see The problem of "the" default shell). Luckily, perl has multi-argument pipe open since 5.8.0:

open(my $pipe,'-|','/usr/local/bin/foo','bar','baz','1&2>3') or die "C +an't start foo: $!"; my @output=<$pipe>; close $pipe or die "Broken pipe: $!";

It's so easy. Granted, it takes two more lines than qx, but we got rid of the default shell. And that two extra lines could easily be wrapped in a function:

my @output=safe_qx('/usr/local/bin/foo','bar','baz','1&2>3');

But, of course, that would be too easy to be true. Why can't we have nice things?

Three-argument pipe open gets the nasty default shell back into play:

> perl -E 'open my $pipe,"-|","pstree --ascii --arguments --long $$ 1> +&2" or die $!;' perl -E open my $pipe,"-|","pstree --ascii --arguments --long $$ 1>&2" + or die $!; `-sh -c pstree --ascii --arguments --long 22176 1>&2 `-pstree --ascii --arguments --long 22176 >

Now what? We could resort to my favorite part of perlipc, "Safe pipe opens". 15 to 28 lines of code just to safely start an external program, and all of that only because perl wants to be clever instead of being safe.


How to fix it

Let's make multi-argument pipe open clever.

system and exec have the indirect-object-as-executable-name hack to prevent the default shell mess. Applying that to open might be possible, but still looks quite hacky:

open $list[0] my $pipe,'-|',@list or die "Can't open pipe: $!";

No! Just no!

So, do we really need to specify the executable twice? We usually don't want to lie to the target program about it's name. It might be useful to make a shell think that it's a login shell, but then again, that can also be done by passing an extra argument. No, we don't want to lie to our child process. If backwards compatibility was not a problem, we could simply disable the shell logic for any pipe open with more than two arguments. But for backwards compatibility, we can't do that. We need is a flag to disable the shell logic.

My first idea was to just double the dash in the MODE argument:

ModeActionUsage of default shell
-|Read from child's STDOUTenabled for three argument open,
disabled for more than three arguments passed to open
(legacy mode)
|-Write to child's STDIN
--|Read from child's STDOUTdisabled
|--Write to child's STDIN

But we still can do better: A single bit is sufficient for a flag. + is already used in MODE, but not in combination with the pipe symbol. So let's use + instead of - to disable the default shell:

ModeActionUsage of default shell
-|Read from child's STDOUTenabled for three argument open,
disabled for more than three arguments passed to open
(legacy mode)
|-Write to child's STDIN
+|Read from child's STDOUTdisabled
|+Write to child's STDIN

Yes, I'm aware that the difference between "+" and "-" in ASCII is two bits.


Update:

For a better mnemonic (ls -f uses "*" to indicate an executable file), we could use "*" instead of "-" to disable the default shell and specify the executable file in the third argument:

ModeActionUsage of default shell
-|Read from child's STDOUTenabled for three argument open,
disabled for more than three arguments passed to open
(legacy mode)
|-Write to child's STDIN
*|Read from child's STDOUTdisabled
|*Write to child's STDIN

Thanks to huck and hippo for finding two missing quotes.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

In reply to Improve pipe open? by afoken

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2022-01-22 20:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    In 2022, my preferred method to securely store passwords is:












    Results (63 votes). Check out past polls.

    Notices?