2

Python provides two convenient functions for calling subprocesses that might fail, subprocess.check_call and subprocess.check_output. Basically,

subprocess.check_call(['command', 'arg1', ...])

spawns the specified command as a subprocess, blocks, and verifies that the subprocess terminated successfully (returned zero). If not, it throws an exception. check_output does the same thing, except it captures the subprocess's stdout and returns it as a byte-string.

This is convenient because it is a single Python expression (you don't have to set up and control the subprocess over several lines of code), and there's no risk of forgetting to check the return value.

What are the idiomatic Ruby equivalents to check_call and check_output? I am aware of the $? global that gives the process's return value, but that would be awkward—the point of having exceptions is that you don't have to manually check error codes. There are numerous ways to spawn a subprocess in Ruby, but I don't see any that provide this feature.

Community
  • 1
  • 1
Mechanical snail
  • 29,755
  • 14
  • 88
  • 113

3 Answers3

1

Here’s a simple check_call I threw together, and that seems to work.

def check_call(*cmd, **kw)
  _, status = Process.waitpid2 Kernel.spawn(*cmd, **kw)
  raise "Command #{cmd} #{status}" unless status.success?
end
andrewdotn
  • 32,721
  • 10
  • 101
  • 130
0

The basic/in-built methods are supplanted by the POpen4 gem. And the shell-executor gem provides further awesomeness.

Subhas
  • 14,290
  • 1
  • 29
  • 37
  • I mentioned "awesomeness" purely on the functional value/benefit to the end user, and not based on where it comes from. But true, in the context of the original question – "idiomatic ruby equivalent" – it's a wrong answer, I didn't interpret the question correctly so it's fine to downvote the answer. Other than that, I have no clue what made you start personal attacks like "expectations low" or "not knowing good languages". This is not a language war here, nobody is comparing ruby and python, and that's completely not the point of the question. – Subhas Oct 06 '19 at 01:42
0

It's hard to say what's the most idiomatic solution in Ruby… but the one that's closest to Python is probably Shell.execute! from shell-executer.

From the example on the docs page:

begin
  Shell.execute!('ls /not_existing')
rescue RuntimeError => e
  print e.message
end

Compare to:

try:
    subprocess.check_call('ls /not_existing', shell=True)
except Exception as e:
    print e.message

The most notable difference here is that the Ruby equivalent doesn't have a way to do shell=False (and take the args as a list), which Python not only has, but defaults to.

Also, Python's e.message will be a default message or something generated based on the return code, while Ruby's e.message will be the child's stderr.

If you want to do shell=False, as far as I know, you'll have to write your own wrapper around something lower level; all of the Ruby wrappers I know of (shell-executer, Popen4, [open4][4]) are wrappers around or emulators of the POSIX popen functions.

abarnert
  • 354,177
  • 51
  • 601
  • 671