質問

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

役に立ちましたか?

解決

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

まず、Ruby がシェルを呼び出すとき、通常は次の呼び出しが行われることに注意してください。 /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 文字は区切り文字であり、任意の文字を使用できます。区切り文字が文字のいずれかの場合 (, [, {, 、 または <、文字通りは、ネストされた区切り文字のペアを考慮して、一致する閉じた区切り文字までのキャラクターで構成されています。他のすべての区切り文字については、文字通りは、区切り文字の次の発生までのキャラクターを構成します。文字列補間 #{ ... } 許可されています。

    バッククォートと同様に、シェル コマンドの結果を返します。

    ドキュメント: http://www.ruby-doc.org/docs/ProgrammingRuby/html/ language.html

    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

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

他のヒント

これに基づくフローチャートは次のとおりです この答え. 。こちらも参照してください。 を使用して script 端末をエミュレートするには.

enter image description here

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

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

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

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

Ruby でのシェル スクリプトの実行について私が考える最高の記事は次のとおりです。」Rubyでシェルコマンドを実行する6つの方法".

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

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

私のお気に入りは オープン3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

これらのメカニズムから選択する場合は、次の点を考慮する必要があります。

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

単純なバックティック (``)、system()、および IO.popen 本格的に Kernel.fork/Kernel.execIO.pipe そして IO.select.

サブプロセスの実行に時間がかかりすぎる場合は、タイムアウトを組み合わせて使用​​することもできます。

残念ながら、それはとても 依存します.

もう 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 構文は複数の分野で機能します Linux, 、マックと ウィンドウズ MS-DOSの初期から。

私は決して Ruby の専門家ではありませんが、試してみます。

$ irb 
system "echo Hi"
Hi
=> true

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

cmd = 'ls'
system(cmd)

上記の回答はすでに非常に素晴らしいものですが、次の概要記事をぜひ共有したいと思います。」Rubyでシェルコマンドを実行する6つの方法"

基本的に、次のことがわかります。

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system そして $?:

system 'false' 
puts $?

バッククォート (`):

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 -- 標準ライブラリ:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 -- 宝石:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

本当に Bash が必要な場合は、「ベスト」回答の注記に従ってください。

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

Bash を使用する必要がある場合は、次を挿入します bash -c "your Bash-only command" 目的の呼び出しメソッド内で。

quick_output = system("ls -la")

quick_bash = system("bash -c 'ls -la'")

テストする:

system("echo $SHELL") system('bash -c "echo $SHELL"')

または、既存のスクリプト ファイルを実行している場合 (例: script_output = system("./my_script.sh")) ルビー すべき シバンを尊重しますが、いつでも使用できます system("bash ./my_script.sh") 確認するために (ただし、 /bin/sh ランニング /bin/bash, 、おそらく気づかないでしょう。

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

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

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

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

最も簡単な方法は、たとえば次のとおりです。

reboot = `init 6`
puts reboot

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

  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"

さまざまな方法でそれを達成できます。

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

exec('ls ~')

使用する backticks or %x

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

使用する Kernel#system コマンド、戻り値 true 成功した場合、 false 失敗して戻る場合 nil コマンドの実行が失敗した場合:

system('ls ~')
=> true

忘れないでください spawn コマンドを使用して、指定されたコマンドを実行するバックグラウンド プロセスを作成します。を使用して完了を待つこともできます。 Process クラスと返されたもの pid:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

ドキュメントは次のように述べています。この方法は次のようなものです #system ただし、コマンドが完了するまでは待機しません。

一般的なケースよりも複雑なケース(これでは処理できない場合)がある場合 ``) チェックアウトしてください Kernel.spawn() ここ. 。これは、によって提供される最も一般的/フル機能のようです。 ストックルビー 外部コマンドを実行します。

例えば。次の目的で使用できます。

  • プロセスグループの作成 (Windows)
  • イン、アウト、エラーをファイル/相互にリダイレクトします。
  • 環境変数、umask を設定する
  • コマンドを実行する前にディレクトリを変更してください
  • CPU/データ/...のリソース制限を設定する
  • 他の回答の他のオプションで実行できることはすべて実行しますが、より多くのコードを使用します。

正式 Rubyのドキュメント には十分な例があります。

env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
command...:
  commandline                 : command line string which is passed to the standard shell
  cmdname, arg1, ...          : command name and one or more arguments (no shell)
  [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
  clearing environment variables:
    :unsetenv_others => true   : clear environment variables except specified by env
    :unsetenv_others => false  : dont clear (default)
  process group:
    :pgroup => true or 0 : make a new process group
    :pgroup => pgid      : join to specified process group
    :pgroup => nil       : dont change the process group (default)
  create new process group: Windows only
    :new_pgroup => true  : the new process is the root process of a new process group
    :new_pgroup => false : dont create a new process group (default)
  resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
    :rlimit_resourcename => limit
    :rlimit_resourcename => [cur_limit, max_limit]
  current directory:
    :chdir => str
  umask:
    :umask => int
  redirection:
    key:
      FD              : single file descriptor in child process
      [FD, FD, ...]   : multiple file descriptor in child process
    value:
      FD                        : redirect to the file descriptor in parent process
      string                    : redirect to file with open(string, "r" or "w")
      [string]                  : redirect to file with open(string, File::RDONLY)
      [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
      [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
      [:child, FD]              : redirect to the redirected file descriptor
      :close                    : close the file descriptor in child process
    FD is one of follows
      :in     : the file descriptor 0 which is the standard input
      :out    : the file descriptor 1 which is the standard output
      :err    : the file descriptor 2 which is the standard error
      integer : the file descriptor of specified the integer
      io      : the file descriptor specified as io.fileno
  file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)
  • backticks ` メソッドは、Ruby からシェルコマンドを呼び出す最も簡単なメソッドです。シェルコマンドの結果を返します。

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`
    

コマンド (attrib など) を指定すると、

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

この方法は、たとえば、system("thecommand") またはバックティック内のコマンド。他のメソッドと比較して、このメソッドの良い点は次のとおりです。たとえばバッククォートでは、実行するコマンドを「配置」したり、実行したいコマンドを変数に保存したりすることはできないようです。また、system("thecommand") では出力を取得できないようです。一方、この方法ではこれらの両方を行うことができ、stdin、stdout、stderr に個別にアクセスできます。

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html

本当の答えではありませんが、おそらく誰かがこれが役に立つと思うでしょう、そしてそれはこれに関連しています。

Windows で TK GUI を使用し、rubyw からシェル コマンドを呼び出す必要がある場合、1 秒未満の間に煩わしい cmd ウィンドウが常にポップアップ表示されます。

これを回避するには、使用できます

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

または

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

どちらも ipconfig の出力を「log.txt」内に保存しますが、ウィンドウは表示されません。

する必要があります require 'win32ole' スクリプト内で。

system(), exec() そして spawn() TK と RubyW を使用すると、迷惑なウィンドウがすべてポップアップ表示されます。

これは、OS X 上の Ruby スクリプトで使用している素晴らしいものです (これにより、ウィンドウから切り替えた後でもスクリプトを開始して更新を取得できます)。

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top