1208

Ruby プログラム内からシェル コマンドを呼び出すにはどうすればよいですか? これらのコマンドからの出力をRubyに戻すにはどうすればよいですか?

4

22 に答える 22

1434

この説明は、私の友人がコメントしたRuby スクリプトに基づいています。スクリプトを改善したい場合は、リンクで自由に更新してください。

まず、Ruby がシェルを呼び出す場合、通常はBash/bin/shではなくを呼び出すことに注意してください。一部の Bash 構文は/bin/sh、すべてのシステムでサポートされていません。

シェルスクリプトを実行する方法は次のとおりです。

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#`、一般にバックティックと呼ばれる –`cmd`

    これは、Bash、PHP、Perl など、他の多くの言語と同様です。

    シェル コマンドの結果 (つまり、標準出力) を返します。

    ドキュメント: http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. 組み込み構文、%x( cmd )

    文字の後にxは区切り文字があり、任意の文字を使用できます。区切り文字が文字([{、またはのいずれか<である場合、リテラルは、ネストされた区切り文字のペアを考慮して、一致する終了区切り文字までの文字で構成されます。他のすべての区切り文字の場合、リテラルは区切り文字が次に現れるまでの文字で構成されます。文字列の補間#{ ... }が許可されています。

    バッククォートと同様に、シェル コマンドの結果 (つまり、標準出力) を返します。

    ドキュメント: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    指定されたコマンドをサブシェルで実行します。

    trueコマンドが見つかって正常に実行された場合は返し、falseそうでない場合は返します。

    ドキュメント: http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    指定された外部コマンドを実行して、現在のプロセスを置き換えます。

    何も返しません。現在のプロセスは置き換えられ、続行されません。

    ドキュメント: http://ruby-doc.org/core/Kernel.html#method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

追加のアドバイスを次に 示します$?。 は と同じ$CHILD_STATUSですが、バッククォートを使用すると、最後にシステムで実行されたコマンドのステータスにアクセスできます。system()または%x{}. exitstatusその後、およびpidプロパティにアクセスできます。

$?.exitstatus

詳細については、次を参照してください。

于 2008-08-05T14:42:45.927 に答える
377

「 Ruby でサブプロセスを起動する各メソッドをいつ使用するか」に基づいたフローチャートを次に示します。「標準出力がパイプではなく端末であるとアプリケーションに思わせる」も参照してください。

ここに画像の説明を入力

于 2016-05-19T17:01:00.407 に答える
165

私がこれを行うのが好きな方法は、%xリテラルを使用することです。これにより、次のように、コマンドで引用符を簡単に (そして読みやすく!) 使用できます。

directorylist = %x[find . -name '*test.rb' | sort]

この場合、現在のディレクトリの下にあるすべてのテスト ファイルがファイル リストに入力され、期待どおりに処理できます。

directorylist.each do |filename|
  filename.chomp!
  # work with file
end
于 2008-08-05T14:08:13.360 に答える
67

Ruby でシェル スクリプトを実行することについて、私の意見では最高の記事は次のとおりです。「Ruby でシェル コマンドを実行する 6 つの方法」。

出力のみを取得する必要がある場合は、バックティックを使用します。

STDOUT や STDERR などのより高度な機能が必要だったので、Open4 gem を使用しました。そこにすべての方法が説明されています。

于 2008-09-02T11:05:10.150 に答える
45

私のお気に入りはOpen3です

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
于 2008-09-18T17:47:37.083 に答える
31

これらのメカニズムから選択する際に考慮すべき点は次のとおりです。

  1. stdoutが必要ですか、それともstderrも必要ですか?それとも分離しましたか?
  2. あなたの出力はどれくらいの大きさですか?結果全体をメモリに保持しますか?
  3. サブプロセスの実行中に出力の一部を読み取りますか?
  4. 結果コードが必要ですか?
  5. プロセスを表し、オンデマンドで強制終了できるRubyオブジェクトが必要ですか?

system()単純なバッククォート( ``) 、、IO.popenから本格的なKernel.fork/ withandまで何でも必要になる場合がKernel.execありIO.pipeますIO.select

サブプロセスの実行に時間がかかりすぎる場合は、タイムアウトをミックスにスローすることもできます。

残念ながら、それは非常に異なります。

于 2008-08-07T05:10:35.627 に答える
27

私は間違いなく Ruby の専門家ではありませんが、試してみます。

$ irb 
system "echo Hi"
Hi
=> true

次のようなこともできるはずです。

cmd = 'ls'
system(cmd)
于 2008-08-05T13:24:16.160 に答える
27

もう 1 つのオプション:

次の場合:

  • stdout だけでなく stderr も必要
  • Open3/Open4 を使用できない/使用しない (Mac の NetBeans で例外がスローされる、理由がわからない)

シェル リダイレクトを使用できます。

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

この2>&1構文は、MS-DOS の初期の頃から、Linux、Mac、およびWindowsで機能します。

于 2010-06-16T02:13:05.643 に答える
12

Perl と同様に、バッククォート演算子 (`) を使用することもできます。

directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory

シンプルなものが必要な場合に便利です。

どの方法を使用するかは、何を達成しようとしているかによって異なります。さまざまな方法の詳細については、ドキュメントを確認してください。

于 2008-08-05T13:57:47.233 に答える
11

複数の方法でそれを達成できます。

を使用するKernel#execと、このコマンドが実行された後は何も実行されません。

exec('ls ~')

使用するbackticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

Kernel#systemコマンドを使用するとtrue、成功したfalse場合は戻り、失敗した場合は戻りnil、コマンドの実行が失敗した場合は戻ります。

system('ls ~')
=> true
于 2012-02-19T18:07:16.460 に答える
11

ここの回答とMihaiの回答にリンクされているものを使用して、これらの要件を満たす関数をまとめました。

  1. STDOUT と STDERR をきちんとキャプチャするので、コンソールからスクリプトを実行しても「リーク」しません。
  2. 引数を配列としてシェルに渡すことができるため、エスケープについて心配する必要はありません。
  3. エラーが発生したときに明確になるように、コマンドの終了ステータスを取得します。

おまけとして、これはシェルコマンドが正常に終了した場合 (0) に STDOUT を返し、STDOUT に何かを置きます。systemこのように、このような場合に単純に戻る とは異なりますtrue

コードは次のとおりです。具体的な機能はsystem_quietly次のとおりです。

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
    err = stderr.gets(nil)
    out = stdout.gets(nil)
    [stdin, stdout, stderr].each{|stream| stream.send('close')}
    exit_status = wait_thread.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
于 2012-02-20T23:36:45.490 に答える