%x[..]. Recently I needed to kick off a system process, so I spent some time working with all 3 options.
Kernel.exec does exactly what the documentation states:
Replaces the current process by running the given external command. If exec is given a single argument, that argument is taken as a line that is subject to shell expansion before being executed. If multiple arguments are given, the second and subsequent arguments are passed as parameters to command with no shell expansion. If the first argument is a two-element array, the first element is the command to be executed, and the second argument is used as the argv value, which may show up in process listings. In MSDOS environments, the command is executed in a subshell; otherwise, one of the exec(2) system calls is used, so the running command may inherit some of the environment of the original program (including open file descriptors).An important thing to notice is that it replaces the current process. To immediately see what is meant by 'replaces', try running exec from irb:
exec "echo *" # echoes list of files in current directory
# never get here
exec "echo", "*" # echoes an asterisk
# never get here
focus:~/work/eaa jay$ irbA more realistic example could be, assume you wanted to execute an external command from rake and you tried to use
irb(main):001:0> exec 'svn st'
exec. Your external command would be executed, but rake would never finish. Not exactly desired results.
Kernel.system behaves very similarly, but does not replace the current process. The documentation states the following:
Executes cmd in a subshell, returning true if the command was found and ran successfully, false otherwise. An error status is available in $?. The arguments are processed in the same way as for Kernel::exec.The same irb test shows us that system will return true or false, and $? can be used to get a Process::Status instance.
irb(main):014:0> s = system 'uptime'Process::Status does contain the exitstatus Fixnum, but it does not appear to capture any errors that may occur.
10:56 up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
irb(main):019:0> s = system "ruby -e 'invalid ruby $%^&'"Though I do like knowing my external application failed, it is nice to know why it failed. If you can redirect the output to a file this isn't a problem; however, you have 2 options if the execution output is simply dumped to stdout. The first option is to use
-e:1: parse error, unexpected $undefined., expecting $
invalid ruby $%^&
systemand redirect output to a file
irb(main):003:0> system 'uptime > results.log'Another option is to use
focus:~/work/eaa jay$ more results.log
13:09 up 4 days, 1:23, 2 users, load averages: 0.21 0.24 0.19
%x[..]and save the results. This option is more often used when the results need to be captured and used to determine further execution flow. To capture the results of a
%x[..]you need to assign a variable to the return value.
irb(main):001:0> result = %x[uptime]Using
=> "13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n"
irb(main):002:0> p result
"13:16 up 4 days, 1:30, 2 users, load averages: 0.39 0.29 0.23\n"
systemallows you to deal with the output when false is returned. However, if you are always going to open the file and process the results you are likely better off using
%x[..]in the first place.