Kernel#system
The simplest way to fire a shell command in a Ruby script is to send a message to Kernel#system.In a nutshell:
- the command is assigned as a String
- the first parameter is the command itself and all following string parameters are the arguments
- executes the command in a subshell
- is a blocking operation (waits until the result of operation completes)
- returns true (for zero exist status), false (for non zero exit status) or nil (if command execution fails)
- The commands result is not available for further processing
- captures exceptions raised from the child process
- the error status (exception) is available in $?, which returns a Process::Status object
system("find *.rb") # script.rb # => true file_name = "script" system("find", "#{file_name}.rb") # script.rb # => true system("find", "*.rb") # find: "*.rb": No such file or directory # => false $? # => #<Process::Status: pid 4438 exit 1>Please note the third command (and compare with the first command): Assigning wild card as a parameter does not work properly.
Kernel#system is great for simple system calls, if the result (e.g. the found files list) is not needed for further processing.
Kernel#exec
Kernel#exec works similar to Kernel#system, with an essential difference: it ends the current process by running the given external command. Running the script.rb:exec("find *.rb") puts "End"will print out script.rb, but not "End". Another example, using IRB:
christian@Trinidad:~$ irb 2.1.1 :001 > exec("date") Sun, 12 Oct 2014 06:40:23 CEST christian@Trinidad:~$exits IRB (the current process), processes the command and returns to bash.
%x() literal
An interpolated shell command can be achieved with the %x() literal as an alternative to the backtick style.In a nutshell:
- the command can be as assigned as a String or even without string notation
- executes the command in a subshell
- is a blocking operation (waits until the result of operation completes)
- returns the command result
- raises exception caused by the child process error
- the error status (exception) is available in $?, which returns a Process::Status object (with qualified exit status)
%x[find *.rb] # => "script.rb" file_name = "script" %x["find #{file_name}.rb"] # => script.rb Time.parse %x[date].chop # => 2014-10-10 02:23:10 +0200 %x[foo] # => Errno::ENOENT: No such file or directory - foo $? # => #<Process::Status: pid 5052 exit 127>for comparison reasons, the same stuff with the backtick notation:
`find *.rb` # => "script.rb" file_name = "script" `find #{file_name}.rb` # => script.rb Time.parse `date`.chop # => 2014-10-10 02:23:10 +0200 `foo` # => Errno::ENOENT: No such file or directory - foo $? # => #<Process::Status: pid 5055 exit 127>The backtick notation is widely used but can be mixed up with String and therefore is not as readable (and intentional) as the %x literal.
Open3#popen3
Another level of granularity is the Open3#popen3.It allows to deal with the input, output, error and even the wait thread.
- the command is assigned as a String
- executes the command in a subshell
- can be a non blocking operation (run other commands while processing the thread)
- full control over the thread
- full control over the stdin, stdout and stderr stream
stdin, stdout, stderr, wait_thr = Open3.popen3('ping www.google.com -c 3') puts "Thread alive? #{wait_thr.alive?}" puts "Some calculation: #{1 + 1}" puts "Output: " + stdout.read puts "Thread alive? #{wait_thr.alive?}" # => Thread alive? true # Some calculation: 2 # PING www.google.com (74.125.136.103) 56(84) bytes of data. # 64 bytes from ea-in-f103.1e100.net (74.125.136.103): icmp_seq=1 ttl=46 time=26.8 ms # 64 bytes from ea-in-f103.1e100.net (74.125.136.103): icmp_seq=2 ttl=46 time=24.7 ms # 64 bytes from ea-in-f103.1e100.net (74.125.136.103): icmp_seq=3 ttl=46 time=23.2 ms # --- www.google.com ping statistics --- # 3 packets transmitted, 3 received, 0% packet loss, time 2001ms # rtt min/avg/max/mdev = 23.229/24.913/26.804/1.477 ms # Thread alive? falsePlease note, that the stdout.read output waited until the thread was successfully ended. That is why there is 2 seconds gap between the calculations output and the stdout.read.
All 3 stdxxx are IO objects. The fourth wait_thr is a Thread object.
Open3#popen3 is the way to deal with system calls, when advanced processing is needed. Especially the full control over its streams is very powerful.
Further articles of interest:
Supported by Ruby 2.1.1
Keine Kommentare:
Kommentar veröffentlichen