web-dev-qa-db-ja.com

Rubyでsystem()呼び出しの出力を取得する

Rubyで Kernel#system を使用してコマンドを呼び出すと、どうやってその出力が得られますか?

system("ls")
296
Macha

私は カオスの答え を少し広げて明確にしたいのですが。

コマンドをバッククォートで囲む場合は、system()を(明示的に)呼び出す必要はまったくありません。バッククォートはコマンドを実行し、出力を文字列として返します。それから、そのように変数に値を代入することができます:

output = `ls`
p output

または

printf output # escapes newline chars
335
Craig Walker

ユーザー指定の値を含む文字列をsystem%x[]などに渡すすべての解決策は安全ではありません。安全ではないということは、ユーザーがコードをコンテキスト内でプログラムのすべての許可を得て実行する可能性があることを意味します。

私が言うことができる限りでは、systemOpen3.popen3だけがRuby 1.8で安全な/エスケープの変形を提供します。 Ruby 1.9ではIO::popenも配列を受け入れます。

すべてのオプションと引数を配列としてこれらの呼び出しの1つに渡すだけです。

終了ステータスだけでなく結果も必要な場合は、おそらくOpen3.popen3を使用します。

require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value

ブロックフォームはstdin、stdout、stderrを自動的に閉じます。そうでなければ、 明示的に閉じられる である必要があります。

詳細はこちら: Rubyで衛生的なシェルコマンドやシステムコールを作成する

234

レコードだけで、両方(出力と演算結果)が必要な場合は、次のようにします。

output=`ls no_existing_file` ;  result=$?.success?
162
FernandoFabreti

これを正しく安全に行う簡単な方法は、 Open3.capture2()Open3.capture2e() 、または Open3.capture3() を使用することです。

Rubyのバッククォートとその%xエイリアスの使用は、信頼できないデータで使用された場合、いかなる環境下でも安全ではありません。それは危険なで、わかりやすく単純です。

untrusted = "; date; echo"
out = `echo #{untrusted}`                              # BAD

untrusted = '"; date; echo"'
out = `echo "#{untrusted}"`                            # BAD

untrusted = "'; date; echo'"
out = `echo '#{untrusted}'`                            # BAD

対照的に、system関数は、正しく使用されていれば引数を正しくエスケープします

ret = system "echo #{untrusted}"                       # BAD
ret = system 'echo', untrusted                         # good

問題は、出力ではなく終了コードが返されることです。後者をキャプチャするのは複雑で面倒です。

このスレッドの最良の答えはこれまでのところOpen3に言及していますが、そのタスクに最適な関数には言及していません。 Open3.capture2capture2eおよびcapture3systemのように機能しますが、2つまたは3つの引数を返します。

out, err, st = Open3.capture3("echo #{untrusted}")     # BAD
out, err, st = Open3.capture3('echo', untrusted)       # good
out_err, st  = Open3.capture2e('echo', untrusted)      # good
out, st      = Open3.capture2('echo', untrusted)       # good
p st.exitstatus

もう1つの言及 IO.popen() 。構文が入力として配列を必要とするという意味では、構文がぎこちなくなることがありますが、これも機能します。

out = IO.popen(['echo', untrusted]).read               # good

便宜上、Open3.capture3()を関数でラップすることができます。

#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
  begin
    stdout, stderr, status = Open3.capture3(*cmd)
    status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
  rescue
  end
end

例:

p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')

次のようになります。

nil
nil
false
false
/usr/bin/which         <— stdout from system('which', 'which')
true                   <- p system('which', 'which')
"/usr/bin/which"       <- p syscall('which', 'which')
59

どのような結果が必要かに応じて、system()または%x []を使用できます。

system()は、コマンドが見つかり正常に実行された場合はtrueを返し、それ以外の場合はfalseを返します。

>> s = system 'uptime'
10:56  up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
=> true
>> s.class
=> TrueClass
>> $?.class
=> Process::Status

一方、%x [..]はコマンドの結果を文字列として保存します。

>> result = %x[uptime]
=> "13:16  up 4 days,  1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> p result 
"13:16  up 4 days,  1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> result.class
=> String

Th Jay Fieldsによるブログ記事 は、system、exec、および%xの使い方の違いについて詳しく説明しています[..]。

59
Martin Gross

別の方法は:

f = open("|ls")
foo = f.read()

"ls"の前の "pipe"文字が開いていることに注意してください。これは、データをプログラムの標準入力に送り込み、その標準出力を読み取るためにも使用できます。

19
dwc

引数をエスケープする必要がある場合、Ruby 1.9では IO.popen も配列を受け入れます。

p IO.popen(["echo", "it's escaped"]).read

以前のバージョンでは、 Open3.popen を使用できます。

require "open3"

Open3.popen3("echo", "it's escaped") { |i, o| p o.read }

Stdinを渡す必要がある場合、これは1.9と1.8の両方で機能するはずです。

out = IO.popen("xxd -p", "r+") { |io|
    io.print "xyz"
    io.close_write
    io.read.chomp
}
p out # "78797a"
19
Lri

あなたはバッククォートを使います:

`ls`
19
chaos

戻り値が必要な場合は、次のものが便利であることがわかりました。

result = %x[ls]
puts result

私は特に私のマシン上のすべてのJavaプロセスのpidをリストしたいと思いました、そしてこれを使いました:

ids = %x[ps ax | grep Java | awk '{ print $1 }' | xargs]
13

SimonHürlimannはすでに説明しましたOpen はbackticksなどよりも安全です。

require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }

ブロックフォームはstdin、stdout、stderrを自動的に閉じます。そうでなければ、 明示的に閉じられる である必要があります。

11
Yarin

バックティックやポペンを使うことはあなたが本当に欲しいものであることが多いのですが、実際にはそれは尋ねられた質問に答えません。 systemの出力をキャプチャするのには正当な理由があるかもしれません(自動テストのためかもしれません)。ちょっとグーグル 答えになった 私は他人のためにここに投稿すると思いました。

実際のsystem呼び出しはテスト対象のコードに埋め込まれているので、テストにこれが必要だったので、私の例では標準出力を取得するためにブロック設定を使用します。

require 'tempfile'

def capture_stdout
  stdout = $stdout.dup
  Tempfile.open 'stdout-redirect' do |temp|
    $stdout.reopen temp.path, 'w+'
    yield if block_given?
    $stdout.reopen stdout
    temp.read
  end
end

このメソッドは、実際のデータを格納するために一時ファイルを使用して、指定されたブロック内のすべての出力を取得します。使用例

captured_content = capture_stdout do
  system 'echo foo'
end
puts captured_content

systemの呼び出しは、内部的にsystemを呼び出すものに置き換えることができます。必要に応じて、同様の方法でstderrをキャプチャすることもできます。

9
Eric Anderson

Kernel#systemを使用して出力をファイルにリダイレクトしたい場合は、次のように変更記述子を指定できます。

追加モードでstdoutとstderrをファイル(/ tmp/log)にリダイレクトします。

system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])

実行時間が長いコマンドの場合、これはリアルタイムで出力を保存します。 IO.pipeを使って出力を保存し、それをKernel#systemからリダイレクトすることもできます。

8
Ashrith

直接システム(...)の置き換えとして、Open3.popen3(...)を使うことができます。

さらなる議論: http://tech.natemurray.com/2007/03/Ruby-Shell-commands.html

7

私はここでこれを見つけることができなかったので、それを加えて、私は全出力を得ることにいくつかの問題を抱えていました。

バックティックを使用してSTDERRをキャプチャしたい場合は、STDERRをSTDOUTにリダイレクトできます。

output = `grep hosts/private/etc/* 2>&1`

ソース: http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-Ruby.html

0
dac2009