我似乎被困在尝试检索从ruby的Open3.popen3()方法启动的shell命令的退出状态.
这是我的代码:
require 'open3'
stdin, stdout, stderr = Open3.popen3('ls')
Run Code Online (Sandbox Code Playgroud)
当我现在尝试访问$?它仍然是nil
毕竟可以检索退出状态吗?
注意:
- ls不是我试图在我的脚本中使用的命令.我只是用它来举个例子.我的脚本有点复杂并且包含用户输入,这就是我需要Open3的清理功能的原因.
- 我也尝试了popen3的块变体,但也没有成功.
我使用popen3得到了意想不到的行为,我想用它来运行像工具ala这样的命令cmd < file1 > file2.以下示例挂起,因此stdout done永远不会到达.使用其他cat可能导致悬挂的工具,从而stdin done永远不会达到.我怀疑,我正在缓冲,但我该如何解决这个问题呢?
#!/usr/bin/env ruby
require 'open3'
Open3.popen3("cat") do |stdin, stdout, stderr, wait_thr|
stdin.puts "foobar"
puts "stdin done"
stdout.each_line { |line| puts line }
puts "stdout done"
puts wait_thr.value
end
puts "all done"
Run Code Online (Sandbox Code Playgroud) 我正在使用Open3的popen3方法来启动一个以类似控制台/ REPL方式运行的进程,以反复接受输入和返回输出.
我可以打开进程,发送输入,并接收输出就好了,代码如下:
Open3.popen3("console_REPL_process") do |stdin, stdout, stderr, wait_thr|
stdin.puts "a string of input"
stdin.close_write
stdout.each_line { |line| puts line } #successfully prints all the output
end
Run Code Online (Sandbox Code Playgroud)
我想连续多次这样做,而不需要重新打开过程,因为启动需要很长时间.
我知道我必须关闭stdin以便stdout返回..但我不知道的是,我如何重新打开'stdin以便我可以写更多输入?
理想情况下,我想做这样的事情:
Open3.popen3("console_REPL_process") do |stdin, stdout, stderr, wait_thr|
stdin.puts "a string of input"
stdin.close_write
stdout.each_line { |line| puts line }
stdin.reopen_somehow()
stdin.puts "another string of input"
stdin.close_write
stdout.each_line { |line| puts line }
# etc..
end
Run Code Online (Sandbox Code Playgroud)
感谢pmoo的回答,我能够使用PTY和设计一个解决方案,并expect期望当进程准备好输入时进程返回的提示字符串,如下所示:
PTY.spawn("console_REPL_process") do |output, input| …Run Code Online (Sandbox Code Playgroud) 我希望运行几个系统命令,并得到以下信息:
不幸的是下面的代码最终是:
/usr/lib/ruby/1.9.1/open3.rb:276:in
read': closed stream (IOError) from /usr/lib/ruby/1.9.1/open3.rb:276:incapture3' 中的块(2 级)
有时,取决于线程调度。例如,当将超时更改为 2 秒(或完全删除超时块)时,代码可以正常工作。
这是示例代码:
require 'open3'
require 'timeout'
def output_from(command)
o, e, s = Open3.capture3(command)
return o
end
attempts = 0
Thread.abort_on_exception = true
for i in 0..5
Thread.new {
begin
Timeout::timeout(0.0001) {
output = output_from('cat /proc/cpuinfo')
}
rescue Timeout::Error => e
attempts+=1
retry unless attempts > 2
end
}
end
puts attempts
Run Code Online (Sandbox Code Playgroud)
我曾尝试rescueinoutput_from方法并关闭 o,但它也没有帮助。我感觉线程以某种方式共享 popen3 实现中的管道或一些变量。
我有一个小的ruby脚本,它在mysql导入方式:mysql -u <user> -p<pass> -h <host> <db> < file.sql但是利用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
Run Code Online (Sandbox Code Playgroud)
这实际上是在做这项工作,但是有一件事困扰着我,关心我希望看到的输出.
如果我将第一行更改为:
mysqlimp = …Run Code Online (Sandbox Code Playgroud) 我正在使用命令行程序,它的工作原理如下:
$ ROUTE_TO_FOLDER/app < "long text"
Run Code Online (Sandbox Code Playgroud)
如果使用参数"app"需要写入"长文本",那么它将填充带有结果的文本文件.如果没有,它将连续用点填充文本文件(我无法处理或修改"app"的代码以避免这种情况).
在ruby脚本中有一行如下:
text = "long text that will be used by app"
output = system("ROUTE_TO_FOLDER/app < #{text}")
Run Code Online (Sandbox Code Playgroud)
现在,如果文本写得很好,就不会有问题,我会得到一个如前所述的输出文件.文本写得不好时会出现问题.接下来发生的是我的ruby脚本挂起,我不知道如何杀死它.
我找到了Open3,我使用了这样的方法:
irb> cmd = "ROUTE_TO_FOLDER/app < #{text}"
irb> stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
=> [#<IO:fd 10>, #<IO:fd 11>, #<IO:fd 13>, #<Thread:0x007f3a1a6f8820 run>]
Run Code Online (Sandbox Code Playgroud)
当我做:
irb> wait_thr.value
Run Code Online (Sandbox Code Playgroud)
它也挂起,并且:
irb> wait_thr.status
=> "sleep"
Run Code Online (Sandbox Code Playgroud)
所以...¿我怎样才能避免这些问题?¿是不是认识到"app"失败了?
提前致谢
我使用以下代码按照类似主题的帖子中的建议发送电子邮件.但邮件尚未发送.有什么建议?
import subprocess
recipient = 'xxxxx@gmail.com'
subject = 'test'
body = 'testing mail through python'
def send_message(recipient, subject, body):
process = subprocess.Popen(['mail', '-s', subject, recipient],
stdin=subprocess.PIPE)
process.communicate(body)
print("sent the email")
Run Code Online (Sandbox Code Playgroud) 目标:我正在用ruby编写一个工作流命令行程序,该程序在UNIX shell上顺序执行其他程序,其中一些程序需要用户输入输入。
问题:虽然我可以成功地处理stdout,并stderr感谢这个有用的博客文章由尼克·查尔顿,不过,我卡在捕捉用户输入,并将其传递到子过程通过命令行。代码如下:
方法
module CMD
def run(cmd, &block)
Open3.popen3(cmd) do |stdin, stdout, stderr, thread|
Thread.new do # STDOUT
until (line = stdout.gets).nil? do
yield nil, line, nil, thread if block_given?
end
end
Thread.new do # STDERR
until (line = stderr.gets).nil? do
yield nil, nil, line, thread if block_given?
end
end
Thread.new do # STDIN
# ????? How to handle
end
thread.join
end
end
end
Run Code Online (Sandbox Code Playgroud)
调用方法
此示例调用shell命令units,该命令提示用户输入度量单位,然后提示您将其转换为单位。这就是它在外壳中的外观
> …Run Code Online (Sandbox Code Playgroud) sum.rb很简单。您输入两个数字,它返回总和。
# sum.rb
puts "Enter number A"
a = gets.chomp
puts "Enter number B"
b = gets.chomp
puts "sum is #{a.to_i + b.to_i}"
Run Code Online (Sandbox Code Playgroud)
robot.rb用于Open3.popen3与sum.rb. 这是代码:
# robot.rb
require 'open3'
Open3.popen3('ruby sum.rb') do |stdin, stdout, stderr, wait_thr|
while line = stdout.gets
if line == "Enter number A\n"
stdin.write("10\n")
elsif line == "Enter number B\n"
stdin.write("30\n")
else
puts line
end
end
end
Run Code Online (Sandbox Code Playgroud)
robot.rb无法运行。似乎它停留在sum.rb's gets.chomp。
后来我发现我必须写如下才能使它工作。您需要事先以正确的顺序为它提供输入。
# robot_2.rb
require 'open3'
Open3.popen3('ruby sum.rb') do …Run Code Online (Sandbox Code Playgroud) 我已经创建了证书颁发机构,并且需要生成并签署50多个证书。我想编写这个过程的脚本。我不想手动输入密码100次以上!
这是我挂断的命令:
openssl req -newkey rsa:1024 -keyout ~/myCA/tempkey.pem -keyform PEM -out ~/myCA/tempreq.pem -outform PEM
Run Code Online (Sandbox Code Playgroud)
问题是,它要我用以下提示创建密码:
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
Run Code Online (Sandbox Code Playgroud)
当系统仅要求我输入密码时,可以使用的-passin pass:mypass命令行选项openssl。但这似乎不适用于创建密码。
另外,当以后我最终将其删除时,需要输入密码似乎很奇怪:
openssl rsa < tempkey.pem > server_key.pem
Run Code Online (Sandbox Code Playgroud)
我尝试创建一个简单的Ruby脚本:
require 'open3'
Open3.popen2("openssl req -newkey rsa:1024 -keyout ~/myCA/tempkey.pem -keyform PEM -out ~/myCA/tempreq.pem -outform PEM") {|i,o,t|
i.puts "mySecretPassword"
i.puts "mySecretPassword"
}
Run Code Online (Sandbox Code Playgroud)
但这似乎也不起作用。我仍然遇到一个手动提示,要求我创建密码。
我正在围绕CLI创建一个Ruby包装器.我发现了一个简洁的方法Open3.capture3(内部使用Open3.popen3),它允许我执行命令并捕获stdout,stderr和退出代码.
我想要检测的一件事是,是否找不到CLI可执行文件(并为此引发特殊错误).我知道UNIX shell 127在找不到命令时会提供退出代码.当我$ foo在bash中执行时,我得到了-bash: foo: command not found,这正是我想要显示的错误消息.
考虑到所有这些,我编写了这样的代码:
require "open3"
stdout, stderr, status = Open3.capture3(command)
case status.exitstatus
when 0
return stdout
when 1, 127
raise MyError, stderr
end
Run Code Online (Sandbox Code Playgroud)
但是,当我运行它时command = "foo",我收到一个错误:
Errno::ENOENT: No such file or directory - foo
/Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:193:in `spawn'
/Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:193:in `popen_run'
/Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:93:in `popen3'
/Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:252:in `capture3'
Run Code Online (Sandbox Code Playgroud)
为什么会出现此错误?我以为Open3.capture3应该直接在shell中执行该命令,为什么我不能得到正常的STDERR并退出代码127?
这个打印1..10两次:
seq 10 > /tmp/ten
perl -e 'fork();seek(STDIN,0,0); print <STDIN>' </tmp/ten
Run Code Online (Sandbox Code Playgroud)
我想使用IPC :: Open3做同样的事情,但我不能让它工作:
perl -MIPC::Open3 -e 'fork();seek(STDIN,0,0); open3(0,1,2,"cat");' < /tmp/ten
perl -MIPC::Open3 -e 'fork();seek(STDIN,0,0); open3(STDIN,STDOUT,STDERR,"cat");' < /tmp/ten
perl -MIPC::Open3 -e 'fork();seek(STDIN,0,0); open3(*STDIN,*STDOUT,*STDERR,"cat");' < /tmp/ten
perl -MIPC::Open3 -e 'fork();seek(STDIN,0,0); open3(\*STDIN,\*STDOUT,\*STDERR,"cat");' < /tmp/ten
Run Code Online (Sandbox Code Playgroud) 我正在制作一个函数,可以读取 Spotify 中当前播放的歌曲的元数据。这是用 lua 编程的,因为它是很棒的 wm 的实现。我得到了以下行来获取我以后可以使用的所有元数据。
handle = io.popen('qdbus org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Metadata | awk -F: \'{$1=\"\";$2=\"\";print substr($0,4)}\'')
Run Code Online (Sandbox Code Playgroud)
但是,当 Spotify 未运行时,我无法获得预期的信息,并且 qdbus 会向 stderr 流写入错误。我想利用 qdbus 写入错误流的事实来确定故障并在那里停止程序。(这还应该捕获与 Spotify 是否正在运行无关的任何其他错误)
我的理解是lua popen使用popen3可以在stdout和stderr之间细分。但到目前为止我的所有努力都是徒劳的,我的错误流总是空的。是否可以检查 stderr 中的非 nil 值以确定对 qdbus (或 awk)的错误调用?
谢谢!