RubyのKernel#systemを使用してコマンドを呼び出す場合、その出力を取得するにはどうすればよいですか?
system("ls")
RubyのKernel#systemを使用してコマンドを呼び出す場合、その出力を取得するにはどうすればよいですか?
system("ls")
回答:
カオスの答えを少し拡大して明確にしたいと思います。
コマンドをバッククォートで囲む場合、(明示的に)system()を呼び出す必要はありません。バッククォートはコマンドを実行し、出力を文字列として返します。その後、次のように値を変数に割り当てることができます。
output = `ls`
p output
または
printf output # escapes newline chars
ls #{filename}
。
command 2>&1
ユーザーを含む文字列を渡すソリューションをすべてに値を提供することに注意してくださいsystem
、%x[]
などの安全ではありません!安全でないとは、実際には、ユーザーがコードをトリガーして、コンテキスト内で実行し、プログラムのすべての権限を付与することを意味します。
私の知る限り唯一の言うことができるようsystem
とOpen3.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を自動的に閉じることに注意してください。そうでない場合、明示的に閉じる必要があります。
gets
呼び出しは引数を渡す必要がありますnil
。そうでない場合、出力の最初の行が取得されるだけです。だから例えばstdout.gets(nil)
。
Open3.popen3
大きな問題がないと思います:パイプが保持できるよりも多くのデータをstdoutに書き込むサブプロセスがある場合、サブプロセスはで中断されstderr.write
、プログラムはスタックしstdout.gets(nil)
ます。
記録のために、(出力と演算結果の両方)が必要な場合は、次のようにすることができます。
output=`ls no_existing_file` ; result=$?.success?
output=`ls no_existing_file 2>&1`; result=$?.success?
正しく確実にこれを行うための簡単な方法は、使用することがある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.capture2
、capture2e
とcapture3
のような作業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')
require 'open3'; output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }
注ブロック形式の意志のオートクローズSTDIN、STDOUTはとstderr-そう、彼らがする必要があるだろうことを明示的にクローズ。
capture2
、capture2e
そしてcapture3
またそれらをstd * sを自動的に閉じます。(少なくとも、私は自分の側でこの問題に遭遇したことはありません。)
Open3#popen2
、popen2e
そしてpopen3
予め定義されたブロックを有する:ruby-doc.org/stdlib-2.1.1/libdoc/open3/rdoc/...
必要な結果の種類に応じて、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詳細に使用して、システム間の違いを説明しています。
引数をエスケープする必要がある場合、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"
あなたはバックティックを使用します:
`ls`
ruby -e '%x{ls}'
-出力なし。(fyi %x{}
はバックティックに相当します。)
sh
すると、出力がコンソール(つまりSTDOUT)にエコーされ、返されます。これはしません。
別の方法は:
f = open("|ls")
foo = f.read()
これは、開いている「ls」の前の「パイプ」文字であることに注意してください。これを使用して、プログラムの標準入力にデータを送り、その標準出力を読み取ることもできます。
戻り値が必要な場合は、以下が役立つことがわかりました。
result = %x[ls]
puts result
私は特に、私のマシン上のすべてのJavaプロセスのPIDをリストしたいと思っていて、これを使用しました:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
バックティックやポペンを使用することは、たいていの場合本当に望んでいることですが、実際に尋ねられた質問に答えることはできません。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
呼び出しは、内部で呼び出すものに置き換えることができますsystem
。stderr
必要に応じて、同様の方法でキャプチャすることもできます。
直接システム(...)の代替として、Open3.popen3(...)を使用できます。
さらなる議論:http : //tech.natemurray.com/2007/03/ruby-shell-commands.html
ここではこれが見つからなかったため、追加すると、完全な出力を取得するのにいくつかの問題が発生しました。
バックティックを使用してSTDERRをキャプチャする場合は、STDERRをSTDOUTにリダイレクトできます。
出力= `grep hosts / private / etc / * 2>&1`
ソース:http : //blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html