April16

Instant Feedback When Running Shell Commands

I came across a need to see the output of a bash shell command in realtime. Usually when you use the backticks or system command you only see the output after the process has run. So in order to get realtime feedback I came up with this:

require 'pty'

def trace_output_for_command(command)
  begin
    PTY.spawn( command ) do |stdin, stdout, pid|
      begin
        stdin.each { |line| print line }
      rescue Errno::EIO
        puts "Output Finished" 
      end
    end
  rescue PTY::ChildExited
    puts "The child process exited!" 
  end
end

trace_output_for_command('ping -c 3 localhost')

When running with MRI / standard ruby this seems to work fine. But when running with jruby instead of exiting after the command has executed it just hung – so I made a small adjustment which fixed that:

When using Jruby:

require 'pty'

def trace_output_for_command(command)
  begin
    PTY.spawn( command ) do |stdin, stdout, pid|
      begin
        stdin.each do |line| 
          print line
          break if stdin.eof?
        end 
      rescue Errno::EIO
        puts "Output Finished" 
      end
    end
  rescue PTY::ChildExited
    puts "The child process exited!" 
  end
end

trace_output_for_command('ping -c 3 localhost')

Finally here is another version which works on unix and returns the exit code of the last process that ran in the shell. Usually I would use $?.exitstatus but it doesn’t seem to be available when using PTY.spawn.

require 'pty'

class ShellCommand

  attr_reader :return_value

  def initialize(command)
    @command = command
    @return_value = 0
  end

  def run_with_trace
    begin
      PTY.spawn( @command + "; echo ${PIPESTATUS[0]}") do |stdin, stdout, pid|
        begin
          stdin.each do |line| 
            puts line
            if stdin.eof?
              @return_value = line.to_i
              break
            end
          end
        rescue Errno::EIO
          puts "Output Finished" 
        end
      end
    rescue PTY::ChildExited
      puts "The child process exited!" 
    end
  end

  def ran_successfully?
    @return_value == 0
  end

end

shell = ShellCommand.new("la -al")
shell.run_with_trace
p shell.ran_successfully?
p shell.return_value

puts "hello this is cool" 
A couple of interesting projects which are related to forking and such:
  • Spoon: http://gist.github.com/321084
  • Popen4 : http://popen4.rubyforge.org/

both installable as gems: gem install spoon open4

Posted by kingsleyh | Filed in Ruby |

Leave a Comment