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


309

RubyのKernel#systemを使用してコマンドを呼び出す場合、その出力を取得するにはどうすればよいですか?

system("ls")


これは非常に手の糸です、ありがとう。コマンドを実行してフィードバックを取得するためのクラスは、サンプルコードに含まれています。
11:15にイルミネート

3
今後のGoogle社員向け。他のシステムコマンドコールとそれらの違いについて知りたい場合は、このSO回答を参照してください
ウズベクジョン

回答:


347

カオスの答えを少し拡大して明確にしたいと思います。

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

output = `ls`
p output

または

printf output # escapes newline chars

4
コマンドの一部として変数を指定する必要がある場合はどうなりますか?つまり、バッククォートが使用される場合、system( "ls" + filename)のようなものは何に変換されますか?
ビジェイDevの

47
通常の文字列の場合と同じように、式の評価を行うことができますls #{filename}
クレイグウォーカー

36
この回答はお勧めできません。無害化されたユーザー入力という新しい問題が発生するためです。
Dogweather 2012

4
@Dogweather:それは本当かもしれませんが、それは他のどの方法とも違いますか?
Craig Walker

20
stderrを取得したい場合は、コマンドの最後に2>&1と入力してください。例:output =command 2>&1
micred

243

ユーザーを含む文字列を渡すソリューションをすべてに値を提供することに注意してくださいsystem%x[]などの安全ではありません!安全でないとは、実際には、ユーザーがコードをトリガーして、コンテキスト内で実行し、プログラムのすべての権限を付与することを意味します。

私の知る限り唯一の言うことができるようsystemOpen3.popen3ルビー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でのサニタリーシェルコマンドまたはシステムコールの形成


26
これは、実際に質問に答え、新しい問題(無害化された入力)を導入することなく問題を解決する唯一の答えです。
Dogweather 2012

2
ありがとう!これは私が望んでいたような答えです。1つの修正:gets呼び出しは引数を渡す必要がありますnil。そうでない場合、出力の最初の行が取得されるだけです。だから例えばstdout.gets(nil)
グレッグ価格

3
stdin、stdout、stderrは、非ブロック形式で明示的に閉じる必要があります。
Yarin、2014年

Ruby 2.0または2.1で何かが変更されたかどうか誰かが知っていますか?編集またはコメントをいただければ
幸いです

1
私は周りの議論にOpen3.popen3大きな問題がないと思います:パイプが保持できるよりも多くのデータをstdoutに書き込むサブプロセスがある場合、サブプロセスはで中断されstderr.write、プログラムはスタックしstdout.gets(nil)ます。
hagello 2014年

165

記録のために、(出力と演算結果の両方)が必要な場合は、次のようにすることができます。

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

4
これはまさに私が探していたものです。ありがとうございました。
JDL

12
これはstdoutのみをキャプチャし、stderrはコンソールに行きます。:標準エラー出力、使用取得するには output=`ls no_existing_file 2>&1`; result=$?.success?
peterept

8
この回答は安全ではないため、使用しないでください。コマンドが定数以外の場合、バックティック構文はバグを引き起こし、セキュリティの脆弱性を引き起こす可能性があります。(そしてそれが定数であっても、後で誰かを定数でないものに使用させ、バグを引き起こす可能性があります。)正しい解決策については、SimonHürlimannの回答を参照してください。
グレッグ価格

23
ユーザー入力をエスケープする必要性について理解するためのGreg Priceへの称賛しかし、書かれたこの答えが安全でないと言うのは正しくありません。言及されたOpen3メソッドはより複雑で、より多くの依存関係を導入します。誰かが「後で非定数に使用する」という議論はわざとらしくありません。確かに、おそらくRailsアプリでは使用しないでしょうが、信頼できないユーザー入力の可能性のない単純なシステムユーティリティスクリプトの場合、バッククォートは完全に問題なく、誰もがそれらを使用することに嫌悪感を抱くべきではありません。
2014年

69

正しく確実にこれを行うための簡単な方法は、使用することがあるOpen3.capture2()Open3.capture2e()またはOpen3.capture3()

信頼できないデータで使用する場合、ルビのバックティックとその%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.capture2capture2ecapture3のような作業systemが、戻って二、三個の引数:

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

別の言及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')

2
これが正解です。また、最も情報が豊富です。唯一足りないのは、std * sのクローズに関する警告です。参照してください。この他のコメントをrequire 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read } 注ブロック形式の意志のオートクローズSTDIN、STDOUTはとstderr-そう、彼らがする必要があるだろうことを明示的にクローズ
Peter H.Boling 14

@ PeterH.Boling:私が知っている最高のcapture2capture2eそしてcapture3またそれらをstd * sを自動的に閉じます。(少なくとも、私は自分の側でこの問題に遭遇したことはありません。)
Denis de Bernardy

ブロック形式を使用しないと、コードベースが何かを閉じる必要があることを知る方法がないため、それらが閉じられていることを強く疑います。それらを閉じないことは短期間のプロセスで問題を引き起こさないため、おそらく問題に遭遇したことはありません。長期実行プロセスを十分に頻繁に再起動すると、std * sを開いていない限り、そこにもottoは表示されません。ループ。Linuxには高いファイル記述子制限があります。これはヒットできますが、ヒットするまで「バグ」は表示されません。
Peter H.Boling、2014年

2
@ PeterH.Boling:いいえ、ソースコードを参照してください。機能だけのラッパされOpen3#popen2popen2eそしてpopen3予め定義されたブロックを有する:ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
デニス・デ・Bernardy

1
@Dennis de Barnardyおそらくあなたが同じクラスのドキュメントにリンクしているのを見逃したかもしれません(Ruby 2.0.0とは異なるメソッドですが 。ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/… 例から: `` `stdin、stdout、stderr、wait_thr = Open3.popen3([env、] cmd ... [、opts])pid = wait_thr [:pid]#開始されたプロセスのpid ... stdin.close#stdin 。。、stdoutとstderrは、このフォームで明示的に閉じる必要がありstdout.closeが`` `私はちょうど文書を引用してstderr.close「#標準入力、stdoutとstderrは、このフォームで明示的にクローズする必要がある。」
ピーター・H. Boling

61

必要な結果の種類に応じて、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

ジェイ・フィールズのブログ記事は [..]、幹部および%X詳細に使用して、システム間の違いを説明しています。


2
%x []を使用するヒントをありがとう。Mac OS Xのルビスクリプトでバックティックを使用していた問題を解決しました。Cygwinを使用してWindowsマシンで同じスクリプトを実行すると、バックティックが原因で失敗しましたが、%x []で動作しました。
Henrik Warne

22

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

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

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

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"

ありがとう!これは完璧です。
グレッグ価格

21

あなたはバックティックを使用します:

`ls`

5
バックティックはターミナルで出力を生成しません。
Mei

3
stderrを生成しませんが、stdoutを提供します。
Nickolay Kondratenko 2013年

1
stdoutやstderrには書き込みません。この例を試してみましょうruby -e '%x{ls}'-出力なし。(fyi %x{}はバックティックに相当します。)
ocodo 2015

これはうまくいきました。を使用shすると、出力がコンソール(つまりSTDOUT)にエコーされ、返されます。これはしません。
Joshua Pinter

19

別の方法は:

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

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


これを使用して、aws cliコマンドからの標準出力を読み取り、jsonを読み取り、 'true'の公式の戻り値を読み取らないでください
kraftydevil

14

戻り値が必要な場合は、以下が役立つことがわかりました。

result = %x[ls]
puts result

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

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

それは素晴らしいソリューションです。
Ronan Louarn 2017


9

バックティックやポペンを使用することは、たいていの場合本当に望んでいることですが、実際に尋ねられた質問に答えることはできません。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

このメソッドは、tempfileを使用して特定のブロックの出力をキャプチャし、実際のデータを格納します。使用例:

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

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


8

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

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

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

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



0

ここではこれが見つからなかったため、追加すると、完全な出力を取得するのにいくつかの問題が発生しました。

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

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

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


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.