2

popen4 を使用して、stdout、stderr、およびコマンド ラインの終了ステータスをキャプチャしています。上記の3つをキャプチャできる限り、popen4に縛られていません。現在、コマンドが見つからないエラーをキャプチャする良い方法が見つかりませんでした。事前タスクで実行できると思いwhich cmdますが、何かが組み込まれていることを望んでいます。

以下では、良いタスク、悪いタスク、偽のタスクを実行して違いを確認できます。私は宝石で新鮮rails new appにこれをやっていますpopen4

#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)

require 'open4'

# returns exit status 0, all is good
task :convert_good do
  puts "convert good"
  `wget https://www.google.com/images/srpr/logo3w.png`
  status = Open4.popen4("convert logo3w.png output.jpg") do |pid, stdin,stdout,stderr|
    stdin.close
    puts "stdout:"
    stdout.each_line { |line| puts line }
    puts "stderr: #{stderr.inspect}"
    stderr.each_line { |line| puts line }
  end
  puts "status: #{status.inspect}"
  puts "exit:   #{status.exitstatus}"
end

# returns exit status 1, we messed up our command
task :convert_bad do
  puts "convert bad"
  status = Open4.popen4("convert logo3w-asdfasdf.png output.jpg") do |pid, stdin,stdout,stderr|
    stdin.close
    puts "stdout:"
    stdout.each_line { |line| puts line }
    puts "stderr: #{stderr.inspect}"
    stderr.each_line { |line| puts line }
  end
  puts "status: #{status.inspect}"
  puts "exit:   #{status.exitstatus}"
end

# I want this to return exit code 127 for command not found
task :convert_none do
  puts "convert bad"
  status = Open4.popen4("convert_not_installed") do |pid, stdin,stdout,stderr|
    stdin.close
    puts "stdout:"
    stdout.each_line { |line| puts line }
    puts "stderr: #{stderr.inspect}"
    #it doesnt like stderr in this case
    #stderr.each_line { |line| puts line }
  end
  puts "status: #{status.inspect}"
  puts "exit:   #{status.exitstatus}"
end

ここに3つのローカル出力があります

# good
stdout:
stderr: #<IO:fd 11>
status: #<Process::Status: pid 17520 exit 0>
exit:   0

# bad arguments
convert bad
stdout:
stderr: #<IO:fd 11>
convert: unable to open image `logo3w-asdfasdf.png': No such file or directory @ blob.c/OpenBlob/2480.
convert: unable to open file `logo3w-asdfasdf.png' @ png.c/ReadPNGImage/2889.
convert: missing an image filename `output.jpg' @ convert.c/ConvertImageCommand/2800.
status: #<Process::Status: pid 17568 exit 1>
exit:   1

# fake command not found, but returns exit 1 and stderr has no lines
convert bad
stdout:
stderr: #<IO:fd 11>
status: #<Process::Status: pid 17612 exit 1>
exit:   1
4

1 に答える 1

5

最初に数点。

  1. 実際にはpopen4 gemを使用していません。これはopen4 gem のラッパーです (少なくとも Unix システムで実行している場合) 。open4 gem を直接使用しています。popen4を使用する場合は、次のように呼び出します。

    status = POpen4.popen4('cmd') do |stdout, stderr, stdin, pid|
      # ...
    end
    
  2. popen4メソッドは、最終的にKernel#execメソッドを介して指定されたコマンドを実行します。その動作は、指定されたコマンドをシェルで実行する必要があるかどうかによって異なります。( http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-execを見ることができますが、あまり役に立ちません。ソース コードの方が適しています。)

例えば:

>  fork { exec "wibble" }
 => 1570 
> (irb):56:in `exec': No such file or directory - wibble (Errno::ENOENT)
    from (irb):56:in `irb_binding'
    from (irb):56:in `fork'
    from (irb):56:in `irb_binding'
    from /Users/evilrich/.rvm/rubies/ree-1.8.7-2011.03/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'
    from :0

ここで、execは存在しないコマンド 'wibble' を直接実行しようとしていたため、例外が発生しました。

> fork { exec "wibble &2>1" }
 => 1572 
> sh: wibble: command not found

ここで、execは私がリダイレクトを使用していることを認識したので、シェルでコマンドを実行しました。違い?STDERR でエラーが発生し、例外はありません。実行するコマンドでシェルを指定することで、強制的にシェルを使用することもできます。

> fork { exec "sh -c 'wibble -abc -def'" }

とにかく、Kernel#execの動作を理解することは、popen4メソッドを希望どおりに動作させるのに役立つ場合があります。

あなたの質問に答えるために、私がpopen4 gem を使用し、(exec のルールによって) シェルで実行されるような方法でコマンドを構築する場合、またはコマンドで「sh -c ...」を使用する場合、次に、あなたが探していると思うような動作を取得します。

> status = POpen4.popen4("sh -c 'wibble -abc -def'") {|stdout, stderr, stdin, pid| puts "Pid: #{pid}"}
Pid: 1663
 => #<Process::Status: pid=1663,exited(127)> 
> puts status.exitstatus
127

アップデート

面白い。Open4.popenは、stderr から読み取ると127 終了ステータスも返します。したがって、popen gem を使用する必要はありません。

> status = Open4.popen4("sh -c 'wibble -abc -def'") {|pid, stdin, stdout, stderr| stderr.read }
 => #<Process::Status: pid 1704 exit 127> 
于 2013-02-17T01:59:04.363 に答える