335

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

system("ls")
4

16 に答える 16

372

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

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

output = `ls`
p output

また

printf output # escapes newline chars
于 2009-10-14T00:06:15.900 に答える
251

systemユーザーが指定した値を含む文字列を%x[]などに渡すすべてのソリューションは安全ではないことに注意してください。安全ではないということは、実際には、ユーザーがコードをトリガーして、コンテキスト内でプログラムのすべての権限を使用して実行する可能性があることを意味します。

私が言うことができる限り、Ruby 1.8 でセキュア/エスケープバリアントを提供しているだけsystemですOpen3.popen3。Ruby 1.9IO::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 でサニタリー シェル コマンドまたはシステム コールを作成する

于 2011-05-11T21:25:34.450 に答える
177

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

output=`ls no_existing_file` ;  result=$?.success?
于 2010-10-17T00:10:39.780 に答える
79

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

Ruby のバッククォートとその%xエイリアスの使用は、信頼できないデータで使用する場合、いかなる状況下でも安全ではありません。それはDANGEROUSであり、単純明快です。

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しますが、2 つまたは 3 つの引数を返します。capture3system

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')
于 2013-11-15T12:53:05.603 に答える
62

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

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

于 2010-11-25T16:04:59.043 に答える
25

引数をエスケープする必要がある場合、Ruby1.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"
于 2012-07-04T10:36:15.203 に答える
21

バックティックを使用します:

`ls`
于 2009-03-27T15:17:37.750 に答える
20

別の方法は次のとおりです。

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

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

于 2009-03-27T15:23:20.810 に答える
14

Simon Hürlimann がすでに説明したように、Open3はバックティックなどよりも安全です。

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

ブロック フォームは標準入力、標準出力、および標準エラー出力を自動的に閉じることに注意してください。それ以外の場合は、明示的に閉じる必要があります。

于 2014-01-12T05:20:05.277 に答える
14

多くの場合、バッククォートまたは popen を使用することは本当に必要なことですが、実際には質問に対する答えにはなりません。出力をキャプチャする正当な理由がある場合があり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ます。

于 2013-05-16T22:47:47.690 に答える
10

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

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

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

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

于 2013-05-26T04:58:41.497 に答える
7

System(...) の直接的な置き換えとして、Open3.popen3(...) を使用できます。

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

于 2010-03-04T14:24:58.507 に答える
-1
puts `date`
puts $?


Mon Mar  7 19:01:15 PST 2016
pid 13093 exit 0
于 2016-03-08T02:35:52.040 に答える