This has bothered me for two days. As I have written an article explaining how do you run a command in ruby and capture the output realtime. I said there are times that PTY will return to PTY::ChildExited problem and I couldn’t find a way of getting PTY’s exit status. Maybe this is because PTY is very poor documented. However, we have a good ruby forum discussing about this problem and helped me a lot in understanding how really PTY works.

The only reason that you might want to use PTY is because it can capture the output realtime. Other than that, PTY is not the best option for you to run a command.

require "pty"
    begin
      pipe_read, pipe_write, pid = PTY.spawn(cmd)
   rescue PTY::ChildExited => e
      puts "The child process exited!"
   end
puts "end"

The above is a typical example how you can use PTY. Pty.spawn method won’t block your script like kernel’s system() call would. It simples creates the child process and the script would continue running. If your script (the parent process) runs too fast and finished earlier than the PTY’s child process. Then the parent process is terminated, thus the PTY’s child process would terminate as well, and ruby doesn’t even have a chance to raise the exception.

So if you wanna wait until PTY’s child process terminate first before running the rest of the script, you can do like the following:

require "pty"
    begin
      PTY.spawn( cmd_ ) do |r, w, pid|
        begin
          r.each { |line| print line;}
       rescue Errno::EIO
       end
     end
   rescue PTY::ChildExited => e
      puts "The child process exited!"
   end
puts "end"

The block given to spawn would handle the output from the child process. Like the above example, it will keep printing the output from running the command. When the command finished running, the block would exit and the script continues running.

If you understand the how PTY works, and knows that you can access Process::Status of the PTY::ChildExited object and get the exit status. Then it is not difficult to come out with that I have at below, which would return the exits status of the process.

begin
      PTY.spawn( cmd_ ) do |r, w, pid|
        begin
          r.each { |line| print line}
        rescue Errno::EIO
        end
      end

      #check exit status
      if $!.nil? || $!.is_a?(SystemExit) && $!.success?
        return 0
      else
        return $!.is_a?(SystemExit) ? $!.status.exitstatus : 1
      end

    rescue PTY::ChildExited => e
      puts "The child process exited."
      return e.status.exitstatus
    rescue => msg
      puts "Exceptions occurred!"
      return 1
    end
  end


2 comments on “PTY::ChildExited exception and PTY’s exit status

  1. Simon Chiang on said:

    I’ve recently fiddled with this myself. I’m pretty sure that, at least on 1.9.2, you can get the exit status by waiting on your PTY process… basically this:

    PTY.spawn(command) do |r,w,pid|
    # …
    Process.wait(pid)
    end

    See here for a more in-depth discussion: http://stackoverflow.com/questions/3874604/pty-spawn-child-exit-code/7263243#7263243

Leave a Reply

Your email address will not be published. Required fields are marked *

*

HTML tags are not allowed.