Running a child process in Ruby (properly)
first part from medium
tl;dr: Common Ruby subprocess patterns
a.k.a enough talk, just tell me what to use!
1 You want to run something, but don’t need its output
Protip: if you want to run a command without arguments, you should actually use:
…because otherwise system
will take your single string to be a shell string.
You want to capture stdout as a string (and inherit stderr):
This is the most common case, in my experience.
(you can also pass a stdin_data: <string>
option if you need to provide some input)
You want to capture stdout as a stream:
… because it might be huge, or you want to process each line as it arrives. This allows you to write to stdin
as a stream, too.
You need to inherit stdin
This is a tricky edge case to figure out from the open3
docs. Each of the functions in that module support the same options as Open3.popen3
. Which says that its options are passed through to Process.spawn
. Which has lots of options for controlling redirections and file descriptors. Unfortunately, the docs don’t mention one crucial point — whatever redirections you pass will be ignored, because popen3
always overrides the redirection options with its own pipes.
So if you do need to inherit stdin
and Kernel#system
won’t do, IO.popen
may be your only choice. e.g. to inherit stdin
and read stdout
as a string:
Bonus round: avoiding deadlocks
There’s one more gotcha when it comes to dealing with subprocesses: deadlocks. This can be an issue when you want to process both stdout
and stderr
of a child. If one of these pipes fill up their OS buffer with unconsumed output, the OS will block the process until somebody reads that buffered data. But if your parent process is busy waiting for the other stream, you’ll get a deadlock. If you do decide to handle both streams yourself, you’ll need to use threads or select
to read from whichever stream has data. But generally the best advice is to just to:
inherit
stderr
or redirect it to a file,combine
stderr
andstdout
viaOpen3.popen2e
or something similar
Starts a list of commands as a pipeline
::pipeline_w starts a list of commands as a pipeline with a pipe which connects to stdin of the first command.