Charlton Wang
12/22/2006 1:53:00 AM
Thanks, Ara,
That clarifies it beautifully. I wasn't aware that Ruby actually looked
behind the scenes for shell characters in order to determine whether or
not to execute the SHELL. I understand the behaviour now. I guess the
original program that caused me to run into this snag was Perforce
(p4). It's clearly using the PWD environment variable to do its work as
is witnessed by:
tcsh> ( setenv PWD dont_exist ; p4 -v3 info | grep cwd )
RpcSendBuffer cwd = dont_exist
Thanks all for clarifying.
Cheers,
Charlton
ara.t.howard@noaa.gov wrote:
> On Thu, 21 Dec 2006, Charlton wrote:
>
> > Hi Nobu,
> >
> > Hm, I'm not sure I understand how this is natural. It's true that the
> > shell sets PWD
>
> and this here is the issue. in this command there is no shell involved:
>
> >>> puts "without semi-colon"
> >>> IO.popen("env").readlines.each do |entry|
> >>> puts entry if entry =~ /PWD/
> >>> end
>
> which you can confirm thusly:
>
> harp:~ > cat a.rb
> IO.popen 'env'
>
> harp:~ > strace -f -- ruby a.rb 2>&1|grep exec
> execve("/home/ahoward/bin/ruby", ["ruby", "a.rb"], [/* 52 vars */]) = 0
> set_thread_area({entry_number:-1 -> 6, base_addr:0xb75e1460, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
> execve("/bin/env", ["env"], [/* 52 vars */]) = 0
> set_thread_area({entry_number:-1 -> 6, base_addr:0xb75e2780, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
>
>
> yet a semi-colon terminated command does indeed invoke /bin/sh:
>
> harp:~ > cat b.rb
> IO.popen 'env;'
>
> harp:~ > strace -f -- ruby b.rb 2>&1|grep exec
> execve("/home/ahoward/bin/ruby", ["ruby", "b.rb"], [/* 52 vars */]) = 0
> set_thread_area({entry_number:-1 -> 6, base_addr:0xb75e0460, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
> execve("/bin/sh", ["sh", "-c", "env;"], [/* 52 vars */]) = 0
> set_thread_area({entry_number:-1 -> 6, base_addr:0xb75e0080, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
> execve("/bin/env", ["env"], [/* 50 vars */]) = 0
> set_thread_area({entry_number:-1 -> 6, base_addr:0xb75e4780, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
>
>
> which you see here
>
> >>> puts "with semi-colon"
> >>> IO.popen("env;").readlines.each do |entry|
> >>> puts entry if entry =~ /PWD/
> >>> end
>
> this is because the command 'env;' is, in fact, not valid. in a c program you
> will not be able to popen it. ruby, however, is kind, when it sees the special
> chars
>
> "*?{}[]<>()~&|\\$;'`\"\n"
>
> in your system call it runs your command via sh. this is doccumented
> somewhere, though i forget where attm...
>
> so what's happening is that, in one case, you exec 'env' which simply inherits
> the parents env, including current value of PWD. in the second case you
> actually exec sh, which sets ENV[PWD], which in turn runs env as a child
> process.
>
> in summary, nobu is right - simply use Dir.pwd and do not rely on auto-magical
> behaviour of child processes which set, or may not set, the PWD env var.
> similarly, if you want to avoid the special handling of cmd strings given to
> system/popen, make sure the commands given are valid (in the 'c' sense) so you
> bypass ruby filtering them via /bin/sh.
>
> regards.
>
> -a
> --
> if you find yourself slandering anybody, first imagine that your mouth is
> filled with excrement. it will break you of the habit quickly enough. - the
> dalai lama