Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyを介してシェルコマンドからstdoutとstderrをキャプチャする

tl; dr Rubyからシェルコマンドを実行し、そのstdout、stderr、およびreturnステータスをキャプチャする場合は、Open3.capture3を確認してください。 方法。 stdoutおよびstderrデータをストリーミング方式で処理する場合は、Open3.popen3を確認してください。 。

非常に多くの悪い選択

Rubyからシェルコマンドを実行する方法は文字通り492あり、それぞれの動作は少し異なります。以下のアプローチのいずれかを使用したに違いありません。私の頼りになるのはいつもバックティックです( ``)。

exec("echo 'hello world'") # exits from ruby, then runs the command
system('echo', 'hello world') # returns the status code
sh('echo', 'hello world') # returns the status code
`echo "hello world"` # returns stdout
%x[echo 'hello world'] # returns stdout

しかし、これらのアプローチはかなり制限されています。シェルコマンドのstdoutだけでなく、そのstderrもキャプチャする必要があるとします。あなたは運が悪かっただけです。または、コマンドの実行が終了したときに一度にすべてではなく、ストリーム内のstdoutデータを処理したいとしますか?運が悪かった。

別のオプションがあります。コマンドを非同期で実行する機能を提供し、stdout、stderr、終了コード、およびPIDを提供するもの。ぜひチェックしてみてください!

Open3

奇妙な名前のopen3モジュールは、Rubyの標準ライブラリの一部です。それは何をしますか?

Open3は、stdout、stderr、終了コード、および別のプログラムの実行時に子プロセスを待機するスレッドへのアクセスを許可します。 Process.spawnの場合と同じ方法で、プログラムのさまざまな属性、リダイレクト、現在のディレクトリなどを指定できます。 (_Source:[Open3 Docs](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html))_

使ったことがない?聞いたことがない?それは、最も友好的な図書館としては外れないからだと思います。名前自体はRubyというよりCのように聞こえます。そして、ドキュメントはかなりハードコアな首ひげです。しかし、試してみると、思ったほど威圧的ではないことがわかります。

caption3

stdout、stderr、およびステータスコードをキャプチャする簡単な方法があった場合はどうなりますか?あります。この記事の残りの部分を読む時間がない場合は、capture3というメソッドを使用して1日で呼び出すことができることを知っておいてください。

例を見てみましょう。現在のディレクトリにあるファイルのリストを取得するとします。これを行うには、lsを実行できます コマンド。

バックティック構文を使用する場合は、次のようになります。

puts(`ls`)

capture3を使用 そのように見えます:

require 'open3'
stdout, stderr, status = Open3.capture3("ls")

これにより、コマンドが実行され、stdoutとstderrが文字列として提供されます。騒ぎも大騒ぎもありません。

セキュリティ

通常、ユーザーがWebサーバー上で任意のコマンドを実行できるようにすることは望ましくありません。そのため、identify #{ params[:filename] }のようなコード とても恐ろしい考えです。

Open3では、コマンドをデータから分離することで、このような問題を回避できます。システム方式と同じように機能します。

Open3.capture3("identify", params[:filename], other_unsafe_params)

popen3

内部的には、capture3はpopen3と呼ばれるはるかに強力な方法を使用します。このメソッドの動作は、system()などのより使い慣れたメソッドとは少し異なります。

外観は次のとおりです。

require 'open3'
Open3.popen3("ls") do |stdout, stderr, status, thread|
  puts stdout.read
end

これは、ファイルを開いて読み取るときのようなものです。次のようなコードを見たことがあると思います:

File.open("my/file/path", "r") do |f|
  puts f.read
end
パイプ

Open3では、stdoutとstderrはすべてパイプであり、ファイルバッファーとよく似た動作をします。そして、ファイルのように、それらを使い終わったら、それらを閉じる必要があります。これがブロック構文の理由です。 (非ブロック構文がありますが、stdoutおよびstderrで手動でcloseを呼び出す必要があります。)

readメソッドは、パイプが閉じられるまで待機してから値を返します。ただし、パイプは、使用可能になったときに読み取りラインもサポートします。シェルコマンドの実行に数秒かかると想像してください。その間、ステータスメッセージをstderrに出力します。それをキャプチャしてユーザーに表示したいと思います。

一度に1行ずつstderrをキャプチャする方法は次のとおりです。

require 'open3'
Open3.popen3("sleep 2; ls") do |stdout, stderr, status, thread|
  while line=stderr.gets do 
    puts(line) 
  end
end
スレッド

まだ話していない議論が1つあります。それがスレッドです。

thread引数は、コマンドの終了を待機しているrubyスレッドへの参照を提供します。現在、コマンドはスレッドで実行されていません。完全に別のプロセスで実行されています。スレッドはプロセスを監視し、完了するまで待機します。

ただし、そのスレッド参照からいくつかの有用なデータを取得できます。

  • thread.pid -シェルコマンドのプロセスIDが含まれます。そのプロセスに対して追加のOSレベルの操作を実行する場合は、これが必要になります。

  • thread.status -プロセスの終了ステータスが含まれます。成功または失敗の場合は1または0。

警告

Open3ドキュメントから:

デッドロックを回避するように注意する必要があります。パイプは固定長のバッファーであるため、[::popen3](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c-popen3)("prog") {| i、o、e、t |プログラムがstderrで生成する出力が多すぎると、o.read}がデッドロックします。 stdoutとstderrを同時に読み取る必要があります(スレッドまたはIO.selectを使用)。ただし、stderr出力が必要ない場合は、[::popen2](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c-を使用できます。 popen2)。 stdoutとstderrの出力のマージに問題がない場合は、[::popen2e](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c-を使用できます。 popen2e)。 stdoutとstderrの出力を別々の文字列として本当に必要とする場合は、[::caption3](https://ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html#method-c -capture3)。

  1. RubyでシンプルなWebSocketサーバーをゼロから構築する

    最近、WebSocketはますます報道されています。彼らは「未来」だと聞いています。 Rails 5のActionCableのおかげで、これまで以上に使いやすくなっていると聞いています。しかし、WebSocketとは正確には何でしょうか。それらはどのように機能しますか? この投稿では、Rubyで単純なWebSocketサーバーを最初から構築することで、これらの質問に答えます。完了すると、ブラウザとサーバー間の双方向通信が実現します。 この投稿のコードは、学習演習として意図されています。実際の本番アプリにWebSocketを実装する場合は、優れたWebSocket-rubygemを確認して

  2. HTTPヘッダーがnginxからRubyアプリに渡される方法

    最近では、ほとんどすべてのWeb開発がフレームワークで行われています。 Rails、Sinatra、Lotusのいずれを使用する場合でも、Cookieやその他のヘッダーがnginxやapacheからアプリケーションサーバーやアプリにどのように渡されるかを考える必要はありません。彼らはただそうします。 この旅をもう少し詳しく見ていきます。ヘッダーのストーリーには、ウェブの歴史に関する興味深い情報がたくさん含まれていることがわかったからです。 とにかくHTTPヘッダーとは何ですか? Webブラウザがrequesnginxtを作成するたびに、HTTPヘッダーと呼ばれるものを送信します。 Cook