Ruby—;Open3.popen3/如何打印输出
我有一个小ruby脚本,它以如下方式执行Ruby—;Open3.popen3/如何打印输出,ruby,popen3,Ruby,Popen3,我有一个小ruby脚本,它以如下方式执行mysql导入:mysql-u-p-h
mysql
导入:mysql-u-p-h
,但使用Open3.popen3
来实现。这就是我到目前为止所做的:
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
然后,整个脚本将永远挂起
我猜,这是因为读和写流互相阻塞,我也猜stdout
需要定期刷新,以便stdin
将继续被消耗。换句话说,只要stdout
的缓冲区已满,进程将等待刷新,但由于这是在最底层首先完成的,因此永远不会发生
我希望有人能证实我的理论?我怎样才能编写这样的代码,既能打印出stdout
中的所有内容,又能将所有内容写入stdin
提前谢谢 - 由于您只向标准输出写入,因此只需使用
即可将Open3#popen2e
和标准输出
合并为一个流stderr
- 要将以换行符结尾的字符串写入流,可以像在简单的hello world程序中使用
一样使用$stdout
put
- 必须使用
或waith_thread.join
等待子进程终止wait_thread.value
- 在任何情况下,如果您想立即看到结果,您都必须启动一个单独的线程来读取流
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
您的代码,已修复:
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
需要“open3”
mysqldump=#。。。
mysqlimp=“mysql-u{mysqllocal['user']}”
mysqlimp无论何时从命令行或通过fork
启动进程,该进程都会从父进程继承stdin、stdout和stderr。这意味着,如果命令行在终端中运行,则新进程的stdin、stdout和stderr将连接到终端
另一方面,popen3不将stdin、stdout和stderr连接到终端,因为您不需要直接的用户交互。所以我们需要别的东西
对于stdin,我们需要具有两种能力的东西:
父进程需要一些东西将子进程从stdin读取时应该获取的数据排队李>
子流程需要像stdin一样提供read
函数的东西
对于stdout和stderr,我们需要类似的东西:
子流程需要写入一些内容put
和print
应该将父进程应该读取的数据排队
父进程需要提供一个read
函数的东西来获取子进程的stdout和stderr数据
这意味着,对于stdin、stdout和stderr,我们需要三个队列(FIFO)来进行父进程和子进程之间的通信。这些队列必须有点像文件,因为它们必须提供读取
,写入
(对于放入
和打印
),关闭
和选择
(数据可用吗?)。
因此,Linux和Windows都提供。这是传统的(本地)进程间通信机制之一。而且,Open3.popen3
确实希望在两个不同的进程之间进行通信。这就是为什么Open3.popen3
将stdin、stdout和stderr连接到匿名管道
每个管道,不管是匿名的还是命名的,都有一个大小有限的缓冲区。此大小取决于操作系统。问题是:如果缓冲区已满,并且某个进程尝试写入管道,则操作系统将挂起该进程,直到另一个进程从管道中读取
这可能是您的问题:
您不断地向子进程提供数据,但不读取子进程写入标准输出的内容李>
因此,子流程的输出在缓冲区中不断累积,直到缓冲区满为止
这是操作系统挂起子进程(put
或print
块)的时候
现在,您仍然可以将数据馈送到连接到子流程的stdin的匿名管道,直到积累了太多的stdin数据。标准管道的缓冲区满了。然后操作系统将挂起父进程(stdin.write
将阻塞)
我建议您使用Open3.capture2e
或类似的Open3.popen3
包装器。您可以使用关键字参数:stdin\u data
将数据传递给子流程
如果坚持与子流程“交互”通信,则需要了解IO。选择或使用多线程。这两个都是相当大的挑战。最好使用Open3.capture*
非常感谢你的回答!关于它的一个问题,在第1行:»[…]只写到stdout[…]«,你的意思是»stdin«,还是我在这里出错了?不,我想说的是,你取子进程(mysql)的stdout和stderr,然后写到ruby的stdout,如果你知道我的意思的话;-)啊,好的!这是有道理的!所以再次感谢你,我会立即试一试!就这样!很好!假设整个过程挂起是因为stout
从未刷新,还是有其他原因?老实说,我不知道:)
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