Ruby's Kernel::exec (and system and %x)

I was reading about Kernel::exec (and the related Kernel::system
function and %x operator).

These routines follow the Perl-ish idiom, which is "If there is one
argument, pass it to the shell; if there is more than one argument,
execute it directly". I call it the Perl-ish idiom simply because that
is the first place I saw this one-exec-routine-to-serve-them-all
behavior.

Now, there is a serious limitation to this, which exists on all
platforms that allow shell meta-characters in file names (space,
asterisk, etc). I usually come across the bug when running
applications on Windows. The problem is this:

  exec("C:\\Program Files\\Anything\\Foo.exe");

Since this is a one-argument call to Kernel::exec, Ruby passes it to
the command interpreter, which tries to split the single argument on
shell meta-characters as it would for "echo *" or "ls -al". However,
"C:\\Program" is not the executable that we desire.

In Perl, one can force the no-shell-interpreter path by calling exec
(and system) like so:

  my $cmd = "C:\\Program Files\\...";
  exec( { $cmd } $cmd );

Is there any such option in Ruby?

-JJ

JJ wrote:

I was reading about Kernel::exec (and the related Kernel::system
function and %x operator).

(...)

Now, there is a serious limitation to this, which exists on all
platforms that allow shell meta-characters in file names (space,
asterisk, etc). I usually come across the bug when running
applications on Windows. The problem is this:

  exec("C:\\Program Files\\Anything\\Foo.exe");

-JJ

Try this:

exec("C://Program Files/Anything/Foo.exe");

···

--
Posted via http://www.ruby-forum.com/\.

  exec("C:\\Program Files\\Anything\\Foo.exe");

Works just fine for me without the semicolon.

Since this is a one-argument call to Kernel::exec, Ruby passes it to
the command interpreter, which tries to split the single argument on
shell meta-characters as it would for "echo *" or "ls -al". However,
"C:\\Program" is not the executable that we desire.

In Perl, one can force the no-shell-interpreter path by calling exec
(and system) like so:

  my $cmd = "C:\\Program Files\\...";
  exec( { $cmd } $cmd );

Is there any such option in Ruby?

-JJ

I might not be understanding your situation, but on my windows system,
this works fine...

s = "c:\\program files\\adobe\\acrobat 7.0\\reader\\acrord32.exe"

=> "c:\\program files\\adobe\\acrobat 7.0\\reader\\acrord32.exe"

`#{s}`

=> ""

exec s also works but the irb process closes after that.

Todd

···

On Jan 6, 2008 2:14 PM, JJ <jjnoakes@gmail.com> wrote:

Try this convolution:

exec('start "" "C:\Program Files\Anything\Foo.exe" ')

[Tested with exec and `` in irb and ruby -e'...']

Using "start" involves some special option parsing in the shell. This
only works with the one-argument form of the exec call, since "start"
is a shell built-in. You have to include the blank argument (window
title override - ignored (I think) when there's no interactive command
shell window).

I believe this works with any path permitted under Windows. AND, it
will work for other languages, including your Perl case above.

Here's a base document on Windows Shell's "Start" command:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/start.mspx?mfr=true

I hope this is handy for anyone used to a *nix platform, launching
executables, and wanting to go cross-platform to Windows.

-Alex

···

On Jan 6, 2008 3:14 PM, JJ <jjnoakes@gmail.com> wrote:

I was reading about Kernel::exec (and the related Kernel::system
function and %x operator).

These routines follow the Perl-ish idiom, which is "If there is one
argument, pass it to the shell; if there is more than one argument,
execute it directly". I call it the Perl-ish idiom simply because that
is the first place I saw this one-exec-routine-to-serve-them-all
behavior.

Now, there is a serious limitation to this, which exists on all
platforms that allow shell meta-characters in file names (space,
asterisk, etc). I usually come across the bug when running
applications on Windows. The problem is this:

  exec("C:\\Program Files\\Anything\\Foo.exe");

Since this is a one-argument call to Kernel::exec, Ruby passes it to
the command interpreter, which tries to split the single argument on
shell meta-characters as it would for "echo *" or "ls -al". However,
"C:\\Program" is not the executable that we desire.

In Perl, one can force the no-shell-interpreter path by calling exec
(and system) like so:

  my $cmd = "C:\\Program Files\\...";
  exec( { $cmd } $cmd );

Is there any such option in Ruby?

-JJ

Hi,

At Mon, 7 Jan 2008 05:14:59 +0900,
JJ wrote in [ruby-talk:286375]:

In Perl, one can force the no-shell-interpreter path by calling exec
(and system) like so:

  my $cmd = "C:\\Program Files\\...";
  exec( { $cmd } $cmd );

  cmd = "C:/Program Files/..."
  exec([cmd, cmd])

Citing from `ri exec':
     If the first argument is a two-element array, the first
     element is the command to be executed, and the second
     argument is used as the +argv[0]+ value, which may show up
     in process listings.

···

--
Nobu Nakada

exec("C://Program Files/Anything/Foo.exe");

heya... you can omit the ; and even the ()

···

--
Posted via http://www.ruby-forum.com/\.

The problem is not the slashes; the problem is how Ruby passes the
string to the operating system. Sorry, but this doesn't help.

-JJ

···

On Jan 6, 6:32 pm, Siep Korteling <s.kortel...@gmail.com> wrote:

JJ wrote:
> I was reading about Kernel::exec (and the related Kernel::system
> function and %x operator).

(...)
> Now, there is a serious limitation to this, which exists on all
> platforms that allow shell meta-characters in file names (space,
> asterisk, etc). I usually come across the bug when running
> applications on Windows. The problem is this:

> exec("C:\\Program Files\\Anything\\Foo.exe");

> -JJ

Try this:

exec("C://Program Files/Anything/Foo.exe");

Just remember that there are length limitations on WinNT 4 servers for
those few that are still using them.

http://support.microsoft.com/kb/186613/en-us

Good Win command to remember though. I don't know if it's necessary, though.

Todd

···

On Jan 7, 2008 1:01 PM, Alex LeDonne <aledonne.listmail@gmail.com> wrote:

Using "start" involves some special option parsing in the shell. This
only works with the one-argument form of the exec call, since "start"
is a shell built-in. You have to include the blank argument (window
title override - ignored (I think) when there's no interactive command
shell window).

I believe this works with any path permitted under Windows. AND, it
will work for other languages, including your Perl case above.

Here's a base document on Windows Shell's "Start" command:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/start.mspx?mfr=true

I hope this is handy for anyone used to a *nix platform, launching
executables, and wanting to go cross-platform to Windows.

-Alex

The problem is that `` and system() make calls to the shell. He is trying to NOT have a call to a shell.

···

On Jan 7, 2008, at 10:11 AM, Todd Benson wrote:

I might not be understanding your situation, but on my windows system,
this works fine...

Try this convolution:

exec('start "" "C:\Program Files\Anything\Foo.exe" ')

What a terrible idea. All I want to do is start a command directly
with no shell in the picture, from Ruby, in a cross-platform way, and
you suggest a Windows-specific workaround with a shell built-in?

I hope this is handy for anyone used to a *nix platform, launching executables, and wanting to go cross-platform to Windows.

I sincerely hope no serious programmer ever uses this suggestion on
Windows, *nix, or anywhere else.

The correct answer here is a simple call in Ruby that executes the
file directly without passing it to a shell interpreter.

-JJ

Thank you. This executes the command directly and doesn't use a sub-
shell. This is exactly what I was looking for.

Thanks,

-JJ

···

On Jan 7, 5:14 pm, Nobuyoshi Nakada <n...@ruby-lang.org> wrote:

Hi,

At Mon, 7 Jan 2008 05:14:59 +0900,
JJ wrote in [ruby-talk:286375]:

> In Perl, one can force the no-shell-interpreter path by calling exec
> (and system) like so:

> my $cmd = "C:\\Program Files\\...";
> exec( { $cmd } $cmd );

  cmd = "C:/Program Files/..."
  exec([cmd, cmd])

Citing from `ri exec':
     If the first argument is a two-element array, the first
     element is the command to be executed, and the second
     argument is used as the +argv[0]+ value, which may show up
     in process listings.

> exec("C:\\Program Files\\Anything\\Foo.exe");

Works just fine for me without the semicolon.

Works just fine with one too. Try to stay on topic.

I might not be understanding your situation, but on my windows system,
this works fine...

> s = "c:\\program files\\adobe\\acrobat 7.0\\reader\\acrord32.exe"
=> "c:\\program files\\adobe\\acrobat 7.0\\reader\\acrord32.exe"
> `#{s}`
=> ""

exec s also works but the irb process closes after that.

This works, but either Ruby or Windows is doing some magic here that
could be dangerous.

Consider this:

  system("C:/Program Files/Internet Explorer/IEXPLORE.exe")

According to Ruby, that is passed to the shell for interpretation. But
if you type the same string in to cmd.exe, you can't run internet
explorer. This means that somewhere along the line, someone is
checking to see which part of the string is the program, regardless of
the spaces. I''m not sure who is doing the parsing (I'd have to check
the Ruby source and then the MSDN docs for the Windows API being
called) but regardless, this isn't the right answer.

Consider this logical next step:

  system("C:/Program Files/Internet Explorer/IEXPLORE.exe http://www.ruby-lang.org")

How does the system know that the program ends at IEXPLORE.exe and the
arguments start at the next space? Someone is "checking" that each
space may or may not designate a program.

Still not convinced that this is hoakey and possibly dangerous? Then
copy Notepad.exe to C:\Program Files\Internet.exe and re-run the same
code again:

  system("C:/Program Files/Internet Explorer/IEXPLORE.exe http://www.ruby-lang.org")

See how the behavior changed based on which part of the space-
separated path existed as a program? Now that "C:/Program Files/
Internet" is executable, it is run instead.

When the files on the system designate which executable is going to be
launched, there is a problem.

This is a dangerous way to program. The correct solution (given by
Nobuyoshi) is this:

  cmd = "C:/Program Files/Internet Explorer/IEXPLORE.exe"
  system([cmd, cmd], "http://www.google.com")

This avoids the sub-shell mess all together, regardless of whether "C:/
Program Files/Internet" is executable.

-JJ

But wouldn't that affect the program cmd, because it is having something passed to it?

···

On Jan 7, 2008, at 5:14 PM, Nobuyoshi Nakada wrote:

  cmd = "C:/Program Files/..."
  exec([cmd, cmd])

Citing from `ri exec':
     If the first argument is a two-element array, the first
     element is the command to be executed, and the second
     argument is used as the +argv[0]+ value, which may show up
     in process listings.

So does giving the full file path automatically make it avoid the shell?

···

On Jan 6, 2008, at 9:46 PM, Marc Heiler wrote:

exec("C://Program Files/Anything/Foo.exe");

We in the rubyunix project are currently working on this right now. What you can you do just pass an empty string to exec, along with the command. This causes it to skip the shell, and instead execute the file directly. IE (for *nix),

fork { exec("ls", "") }

···

On Jan 7, 2008 1:01 PM, Alex LeDonne <aledonne.listmail@gmail.com> > wrote:

Using "start" involves some special option parsing in the shell. This
only works with the one-argument form of the exec call, since "start"
is a shell built-in. You have to include the blank argument (window
title override - ignored (I think) when there's no interactive command
shell window).

I believe this works with any path permitted under Windows. AND, it
will work for other languages, including your Perl case above.

Here's a base document on Windows Shell's "Start" command:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/start.mspx?mfr=true

I hope this is handy for anyone used to a *nix platform, launching
executables, and wanting to go cross-platform to Windows.

Which I don't really see the reason for. I know I spouted off in
another thread about "standing on the shoulders of others", but I do
think it's alright to trust certain types of software. Point being...

Calls to a shell -- of your choice -- shouldn't be considered a bad thing.

Todd

···

On Jan 7, 2008 3:07 PM, thefed <fedzor@gmail.com> wrote:

The problem is that `` and system() make calls to the shell. He is
trying to NOT have a call to a shell.

I believe:

system(cmd, "http://www.google.com")

would also skip the shell entirely, since you are providing more than one argument to system.

The problem is that when you don't want to pass any arguments to the program you are left with a single argument to system (or exec), which triggers the shell expansion algorithm. As you illustrated with 'Program Files', this is a problem if your cmd path includes characters that the shell is interested in (such as spaces). As Nobuyoshi pointed out, the way out of that is to pass an array instead of a single argument.

I just wanted to point out that if you are passing arguments to the program you can use:

   system(cmd, arg1, arg2)

instead of

   system([cmd, cmd], arg1, arg2)

and still avoid the shell. Probably safer to use the second syntax though since if
you are doing something like:

   system(cmd, *args)

You may in fact end up passing just a single argument and invoke the shell again.

Seems like a dodgy sort of design to me. I gather it comes from trying to
merge system() and exec() from the C library, probably for systems that
don't have a concept of fork(). From JJ's post, I gather that Perl might
be the source of this chimera.

Gary Wright

···

On Jan 8, 2008, at 10:50 AM, JJ wrote:

This is a dangerous way to program. The correct solution (given by
Nobuyoshi) is this:

  cmd = "C:/Program Files/Internet Explorer/IEXPLORE.exe"
  system([cmd, cmd], "http://www.google.com")

This avoids the sub-shell mess all together, regardless of whether "C:/
Program Files/Internet" is executable.

How would one pass args here, though?
exec( ["/usr/bin/ls", "ls"], "-al")

Is that right?

···

On Jan 8, 2008, at 9:59 AM, JJ wrote:

On Jan 7, 5:14 pm, Nobuyoshi Nakada <n...@ruby-lang.org> wrote:

Hi,

At Mon, 7 Jan 2008 05:14:59 +0900,
JJ wrote in [ruby-talk:286375]:

In Perl, one can force the no-shell-interpreter path by calling exec
(and system) like so:

  my $cmd = "C:\\Program Files\\...";
  exec( { $cmd } $cmd );

  cmd = "C:/Program Files/..."
  exec([cmd, cmd])

Citing from `ri exec':
     If the first argument is a two-element array, the first
     element is the command to be executed, and the second
     argument is used as the +argv[0]+ value, which may show up
     in process listings.

Thank you. This executes the command directly and doesn't use a sub-
shell. This is exactly what I was looking for.

No. This code

  exec([cmd, cmd])

is running cmd with no arguments. The second occurrence of "cmd" is
what is actually passed in to the process as its program name
(available to C programs via argv[0] in main(), for example).

So with the two-item list, there is no shell interpretation. To add
arguments, use

  exec([cmd, cmd], arg, arg)

but that is the same as

  exec(cmd, arg, arg)

simply because once you have at least two arguments to "exec", it
behaves in the no-shell fashion anyhow.

One could "trick" a program by running

  exec([cmd, "string"], arg, arg)

and the program would execute, but see "string" as how it was invoked.
This confuses some applications, but is possible.

-JJ

···

On Jan 8, 4:41 pm, thefed <fed...@gmail.com> wrote:

On Jan 7, 2008, at 5:14 PM, Nobuyoshi Nakada wrote:

> cmd = "C:/Program Files/..."
> exec([cmd, cmd])

> Citing from `ri exec':
> If the first argument is a two-element array, the first
> element is the command to be executed, and the second
> argument is used as the +argv[0]+ value, which may show up
> in process listings.

But wouldn't that affect the program cmd, because it is having
something passed to it?

We in the rubyunix project are currently working on this right now.
What you can you do just pass an empty string to exec, along with the
command. This causes it to skip the shell, and instead execute the
file directly. IE (for *nix),

fork { exec("ls", "") }

This is a bad idea - an empty argument means something different from
no argument at all.

Try this if you don't believe me:

  echo foo | cat
  echo foo | cat ""

So no, adding an extra empty argument is not a good solution.

-JJ