New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backticks don't give exception for nonexistant command #1396

Closed
shawnjgoff opened this Issue Jan 10, 2014 · 7 comments

Comments

Projects
None yet
4 participants
@shawnjgoff

shawnjgoff commented Jan 10, 2014

MRI raises an ENOENT exception when using backtics with a nonexistant command. Jruby 1.7.8 on JRE 1.7.0_45 returns an empty string.

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius Jan 10, 2014

Member

Does not seem specific to our Java 7 backtick logic, but the Java 6 logic does at least provide the execution error as a string:

system ~/projects/jruby-1.7 $ pickjdk 5 ; jruby -e 'puts `blahblah`'
New JDK: jdk1.7.0_45.jdk


system ~/projects/jruby-1.7 $ pickjdk 1 ; jruby -e 'puts `blahblah`'
New JDK: 1.6.0.jdk
/bin/sh: blahblah: command not found


system ~/projects/jruby-1.7 $ ruby-1.9.3 -e 'puts `blahblah`'
-e:1:in ``': No such file or directory - blahblah (Errno::ENOENT)
    from -e:1:in `<main>'
Member

headius commented Jan 10, 2014

Does not seem specific to our Java 7 backtick logic, but the Java 6 logic does at least provide the execution error as a string:

system ~/projects/jruby-1.7 $ pickjdk 5 ; jruby -e 'puts `blahblah`'
New JDK: jdk1.7.0_45.jdk


system ~/projects/jruby-1.7 $ pickjdk 1 ; jruby -e 'puts `blahblah`'
New JDK: 1.6.0.jdk
/bin/sh: blahblah: command not found


system ~/projects/jruby-1.7 $ ruby-1.9.3 -e 'puts `blahblah`'
-e:1:in ``': No such file or directory - blahblah (Errno::ENOENT)
    from -e:1:in `<main>'
@shawnjgoff

This comment has been minimized.

Show comment
Hide comment
@shawnjgoff

shawnjgoff Jan 16, 2014

With a little more experimentation, it looks like any nonzero exit status returns an empty string, no matter if there was something on stdout or not. In MRI, you get the stdout from the command even if there was a nonzero exit status.

shawnjgoff commented Jan 16, 2014

With a little more experimentation, it looks like any nonzero exit status returns an empty string, no matter if there was something on stdout or not. In MRI, you get the stdout from the command even if there was a nonzero exit status.

@masover

This comment has been minimized.

Show comment
Hide comment
@masover

masover May 1, 2014

As of 1.7.12, the behavior I've observed is that the exit status is actually completely ignored. And you still get the stdout, but the stderr is completely ignored. This is identical to the MRI behavior (2.1.1), except MRI apparently lets stderr pass right through to my terminal -- the program still doesn't see that error, but at least I can see it this time.

There's still the difference with getting an ENOENT or not. I would guess what's happening here is that Ruby can just call the native C stuff like execvp(), which handles the PATH environment variable without forcing you to actually launch a shell, and that's where the ENOENT comes from. But for more complicated commands, you have to use the shell anyway:

irb(main):007:0> `notfound < /dev/null`
sh: 1: notfound: not found
=> ""

As far as I can tell, since it's the shell reporting the error, it shows up on stderr, gets passed through to my terminal, and the program just gets an empty string. So I would guess what happens here is JRuby always uses /bin/sh (even for commands we could handle without it), and it always discards stderr, instead of letting it through to the terminal.

I'm learning that if I care about whether a command actually ran, backticks are probably a bad idea. Still, would it be useful to duplicate MRI's behavior here?

masover commented May 1, 2014

As of 1.7.12, the behavior I've observed is that the exit status is actually completely ignored. And you still get the stdout, but the stderr is completely ignored. This is identical to the MRI behavior (2.1.1), except MRI apparently lets stderr pass right through to my terminal -- the program still doesn't see that error, but at least I can see it this time.

There's still the difference with getting an ENOENT or not. I would guess what's happening here is that Ruby can just call the native C stuff like execvp(), which handles the PATH environment variable without forcing you to actually launch a shell, and that's where the ENOENT comes from. But for more complicated commands, you have to use the shell anyway:

irb(main):007:0> `notfound < /dev/null`
sh: 1: notfound: not found
=> ""

As far as I can tell, since it's the shell reporting the error, it shows up on stderr, gets passed through to my terminal, and the program just gets an empty string. So I would guess what happens here is JRuby always uses /bin/sh (even for commands we could handle without it), and it always discards stderr, instead of letting it through to the terminal.

I'm learning that if I care about whether a command actually ran, backticks are probably a bad idea. Still, would it be useful to duplicate MRI's behavior here?

@jsvd

This comment has been minimized.

Show comment
Hide comment
@jsvd

jsvd May 7, 2015

On a further note, jruby-1.7.19 still has this behavior, and while in MacOSX/Linux + JDK7/8 a non existant command returns "", the same jruby version on Windows will properly raise an exception:

irb(main):002:0> `command_that_doesnt_exist`
Errno::ENOENT: No such file or directory - command_that_doesnt_exist
        from file:/C:/logstash-1.5.0-rc3/vendor/jruby/lib/jruby.jar!/jruby/kerne
l/jruby/process_manager.rb:24:in ``'
        from file:/C:/logstash-1.5.0-rc3/vendor/jruby/lib/jruby.jar!/jruby/kerne
l/jruby/process_manager.rb:49:in ``'
        from (irb):2:in `evaluate'

The behaviour in both environments should be consistent (and ideally also between JRuby and MRI).

jsvd commented May 7, 2015

On a further note, jruby-1.7.19 still has this behavior, and while in MacOSX/Linux + JDK7/8 a non existant command returns "", the same jruby version on Windows will properly raise an exception:

irb(main):002:0> `command_that_doesnt_exist`
Errno::ENOENT: No such file or directory - command_that_doesnt_exist
        from file:/C:/logstash-1.5.0-rc3/vendor/jruby/lib/jruby.jar!/jruby/kerne
l/jruby/process_manager.rb:24:in ``'
        from file:/C:/logstash-1.5.0-rc3/vendor/jruby/lib/jruby.jar!/jruby/kerne
l/jruby/process_manager.rb:49:in ``'
        from (irb):2:in `evaluate'

The behaviour in both environments should be consistent (and ideally also between JRuby and MRI).

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius May 7, 2015

Member

Works correctly in JRuby 9k but still broken on jruby-1_7 branch as of today.

Member

headius commented May 7, 2015

Works correctly in JRuby 9k but still broken on jruby-1_7 branch as of today.

@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius May 7, 2015

Member

So this is a bit gnarly. The logic for ` lives in process_manager.rb and is implemented in Ruby using Java integration. It calls into LaunchConfig to check if we should use sh for the backtick, which returns true unconditionally when not running on Windows. That causes it to use sh, which doesn't find the command, but stderr appears not to be hooked up.

Note that MRI is a bit inconsistent here too...if you have a bad command with any shell characters nearby, it will switch to running with sh and NOT raise any error:

[] ~/projects/ruby $ rvm ruby-1.9 do ruby -e '`boop $FOO`; puts "ok!"'
sh: boop: command not found
ok!
Member

headius commented May 7, 2015

So this is a bit gnarly. The logic for ` lives in process_manager.rb and is implemented in Ruby using Java integration. It calls into LaunchConfig to check if we should use sh for the backtick, which returns true unconditionally when not running on Windows. That causes it to use sh, which doesn't find the command, but stderr appears not to be hooked up.

Note that MRI is a bit inconsistent here too...if you have a bad command with any shell characters nearby, it will switch to running with sh and NOT raise any error:

[] ~/projects/ruby $ rvm ruby-1.9 do ruby -e '`boop $FOO`; puts "ok!"'
sh: boop: command not found
ok!
@headius

This comment has been minimized.

Show comment
Hide comment
@headius

headius May 8, 2015

Member

This should be fixed now. I duplicated logic from popen that does similar bin checking.

Member

headius commented May 8, 2015

This should be fixed now. I duplicated logic from popen that does similar bin checking.

@headius headius closed this May 8, 2015

@headius headius added this to the JRuby 1.7.21 milestone May 8, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment