tags:

views:

389

answers:

1

Here's some Ruby code:

puts %x{ pstree #{$$} }   # never forks
puts %x{ pstree '#{$$}' } # forks on amd64 only

On 32-bit Ubuntu Dapper, I get this output:

t.rb---pstree
t.rb---pstree

Which makes sense to me. But on 64-bit Ubuntu Hardy, I get this:

t.rb---sh---pstree
t.rb---pstree

What's being shown here is that Ruby forks before exec'ing in just one of the cases. When I put the code in a file and run it under strace -fF, it appears that on 64-bit Hardy it calls clone() (like fork()) before execve(), whereas on 32-bit Dapper it does no such thing.

My Ruby versions are:

ruby 1.8.4 (2005-12-24) [i486-linux]
ruby 1.8.6 (2007-09-24 patchlevel 111) [x86_64-linux]

I should try mixing & matching interpreters & OS's & word sizes more, but right now it's not easy since I don't administer these machines. Maybe someone among you can tell me what the difference even is between these commands on the 64-bit system, let alone why they work the same on the 32-bit one.

+2  A: 

Ruby performs shell expansion when %x is used with a single argument (like you are doing).

Here's my guess as to what is going on:

Ruby scans the command to determine if there are any special characters that would result in the need to perform shell expansion, if so it calls the shell to do that. In the second example the single quotes are enough to make Ruby want to call the shell to do the expansion, hence the fork. In the first example Ruby can determine that shell expansion is not needed as the command contains no special characters (after variable expansion), hence no fork. The difference between the two versions probably has to do with an internal change in how ruby tries to determine is shell expansion is needed. I get a fork for the second example on ruby 1.8.5 on a 32-bit machine.

[EDIT]

Okay, I took a look at the source code for ruby 1.8.4 and 1.8.6 and both versions use the same criteria to determine whether or not to call a shell to perform shell expansion, if any of the following characters exist in the command line the shell will be invoked when one argument to %x is provided:

*?{}[]<>()~&|\\$;'`"\n

Ruby is actually calling the shell in both cases (in example that contains the quotes), the reason you are seeing different outputs from pstree is due to differences in the the sh command on the different machines, one calls fork, the other doesn't. To see this for yourself, run this command on both machines:

/bin/sh -c "pstree $$"

This is the command that Ruby is using to execute pstree in the example with quotes on both machines. You should see bash---pstree on the 32-bit machine and bash---sh---pstree on the other one.

So now I'm curious, what led you to discover this difference and is it causing a problem?

Robert Gamble
I discovered it because it caused a problem in a Ruby program which would launch another program and use `pgrep` to check if it was running. This stopped working correctly on the 64-bit platform--`pgrep` always returned something. It seems that `dash` (new shell in Ubuntu Hardy) is to blame.
John Zwinck
Anyway, it seems you are correct about the quoting; removing the quotes gives the desired behavior on both platforms. Thank you.
John Zwinck