rubypopen3

Open3.popen3 / how to print the output


I have a little ruby script which does a mysql import in the way: mysql -u <user> -p<pass> -h <host> <db> < file.sql, but utilizes Open3.popen3 to do so. That is what I have so far:

mysqlimp = "mysql -u #{mysqllocal['user']} "
mysqlimp << "-h #{mysqllocal['host']} "
mysqlimp << "-p#{mysqllocal['pass']} "
mysqlimp << "#{mysqllocal['db']}"

Open3.popen3(mysqlimp) do |stdin, stdout, stderr, wthr|
  stdin.write "DROP DATABASE IF EXISTS #{mysqllocal['db']};\n"
  stdin.write "CREATE DATABASE #{mysqllocal['db']};\n"
  stdin.write "USE #{mysqllocal['db']};\n"

  stdin.write mysqldump #a string containing the database data
  stdin.close

  stdout.each_line { |line| puts line }
  stdout.close

  stderr.each_line { |line| puts line }
  stderr.close
end

That is actually doing the Job, but there is one thing that bothers me, concerned to the output I would like to see.

If I change the first line to:

mysqlimp = "mysql -v -u #{mysqllocal['user']} " #note the -v

then the whole script hangs forever.

I guess, that happens because the read- and write-stream block each other and I also guess that the stdout needs to be flushed regularly so that stdin will go on to be consumed. In other words, as long as the buffer of the stdout is full, the process will wait until its flushed, but since this is done at the very bottom first, that never happens.

I hope someone can verify my theory? How could I write code that does prints out everything from the stdout and writes everything to the stdin as well?


Solution

  • Example:

    require 'open3'
    
    cmd = 'sh'
    
    Open3.popen2e(cmd) do |stdin, stdout_stderr, wait_thread|
      Thread.new do
        stdout_stderr.each {|l| puts l }
      end
    
      stdin.puts 'ls'
      stdin.close
    
      wait_thread.value
    end
    

    Your code, fixed:

    require 'open3'
    
    mysqldump = # ...
    
    mysqlimp = "mysql -u #{mysqllocal['user']} "
    mysqlimp << "-h #{mysqllocal['host']} "
    mysqlimp << "-p#{mysqllocal['pass']} "
    mysqlimp << "#{mysqllocal['db']}"
    
    Open3.popen2e(mysqlimp) do |stdin, stdout_stderr, wait_thread|
      Thread.new do
        stdout_stderr.each {|l| puts l }
      end
    
      stdin.puts "DROP DATABASE IF EXISTS #{mysqllocal['db']};"
      stdin.puts "CREATE DATABASE #{mysqllocal['db']};"
      stdin.puts "USE #{mysqllocal['db']};"
      stdin.close
    
      wait_thread.value
    end