We often interact with the operating system or execute Shell commands in Ruby. Ruby provides us with many ways to accomplish this task.
Exec
Kernel#exec
Replaces the current process by executing a given command, for example:
$ irb>> exec ‘echo "hello $HOSTNAME"‘hello codefun$
Note exec
echo
Replace the process with the command irb
, and then exit. Because Ruby actually ends the method, it can only be used in a limited way. The disadvantage of this approach is that you cannot know from a Ruby script whether a command succeeds or fails.
System
system
The command exec
is similar to the operation, but it is executed in Subshell instead of replacing the current process. exec
system
give us more information than that. If the command executes successfully, it returns true, otherwise false.
$irb>> system ‘echo "hello $HOSTNAME"‘ hello codefun=> true>> system ‘false‘=> false>> puts $?256=> nil>>
system
Sets the exit state of the process to a global variable $?
. Note false
The exit status of the command, which is always a value other than 0. Checking the exit code allows us to throw (raise) exceptions and retry the (retry) command.
Novice Note: The Unix command executes a successful exit code of 0, otherwise it is not 0.
If we want to know, "is the command executed successfully?" "It's system
good to use. However, we often want to capture the output of the command and use it in the program.
Backticks (')
Backticks (also called "Backquotes") executes the command in Subshell and returns the standard output from the command.
$ irb>> today = `date`=> "Wed Jul 4 22:03:15 CST 2012\n">> $?=> #<Process::Status: pid 6169 exit 0>>> $?.to_i=> 0
This is perhaps the most widely known method of executing commands in Subshell. As you can see, it returns the output of the command, and then we use it like any other string.
Note The $?
integer that is not the return state, but actually the Process::status object. Not only did we get the exit status, but we also had the process ID. Process::Status#to_i
returns the exit status of the integer type ( #to_s
returns the exit status of the string type).
With Backticks we can only get the standard output (stdout) of the command, but not its standard error (STDERR). In the following example, we execute a Perl script to output a string to a standard error.
$ irb>> warning = `perl -e "warn ‘dust in the wind‘"`dust in the wind at -e line 1.=> "">> puts warning=> nil
Note that the variable warning is not set. When we execute warn in Perl
, the standard error output generated is not captured by Backticks.
Io#popen
IO#popen
is another way to execute a command in a child process. popen
gives you more control, both the standard input and standard output of the child process are connected to the IO object.
$ irb>> IO.popen("date") { |f| puts f.gets }Wed Jul 4 22:02:31 CST 2012=> nil
IO#popen
It's good, but I usually use it when this kind of subdivision is needed Open3#popen3
.
Open3#popen3
The Ruby standard library contains the Open3 class. It is easy to use and can return standard input, standard output, and standard errors. In this case, let's use the interactive command dc
. The DC is the Inverse Wave calculator (reverse-polish calculator) that is read from the standard input. Let's push two numbers and an operator into the stack first. Then we use it p
to print out the results. Below we push 5, 10 and +, the result standard output obtains 15.
$ irb>> require' Open3 '= True>> stdin, stdout, stderr =Open3.popen3 (' DC ')= [#<io:fd 6>#<io:fd 7>#<io:fd 9>#<thread:0x816d46c Sleep>]>> stdin.puts (5) => nil>> Stdin.puts (10) => Nil>> stdin.puts ( "+") => nil >> stdin.puts ( "P") => nil>> stdout.gets=> " 15\n "
Using this command, we can not only read the output of the command, but also write to the standard input of the command. This allows us the flexibility to handle interactions with commands.
If we need to, we popen3
will also give us standard mistakes.
# (irb continued...)>> stdin.puts("asdfasdfasdfasdf")=> nil>> stderr.gets=> "dc: stack empty\n"
The disadvantage of using POPEN3 is that the $?
appropriate exit status is not returned.
$ irb>> require ' Open3 ' => true >> stdin, stdout, stderr = Open3.popen3 ( ' false ') => [#<io:fd 8>, #<io:fd 10>, #<IO: FD 12>, #<thread:0x8297644 sleep>] >> $? => nil>> $?" To_i=> 0
0? False is assumed to return a non-0 exit state. This shortcoming takes us to Open4.
Open4#popen4
Open4#popen4
Is the Ruby Gem created by Ara Howard. Its operation open3
is similar, except that we can get the exit status from the program. popen4
returns the process ID for Subshell so that we can get the exit status from the waiting process. (You need to install OPEN4 gem)
$ irb>> require' Open4 '= True>> PID, stdin, stdout, stderr =OPEN4::p Open4"False"= [17913,#<io:fd 6>#<io:fd 7>#<IO:FD 9>] >> $? => nil>> pid=> 17913>> ignored, status = process::waitpid2 pid => [17913, #<process::status:pid 17913 exit 1>] >> status.to_i=> 256
You can also call it as a block popen4
and it will automatically wait for the exit state.
$ irb>> require' open4 '= True>> status = Open4::p open4 ("false") do |pid, stdin, stdout, stderr|? > puts "pid #{pid}">> endPID 18535= #<process::status:pid 18535 exit 1& Gt >> puts Statuspid 18535 exit 1= nil