Techioz Blog

Rubyにおけるプロセス置換(コマンド置換ではない)

概要

bash、zsh、ksh88、ksh93 および同様のシェルでは、指定されたコード部分が含まれるファイル名 (プラットフォームに応じて /dev/fd エントリまたは名前付きパイプとして実装される) を簡単に生成できます。サブシェルで実行されます。

同じことをRubyインタプリタで行うことに興味があります。引数の1つであるファイル名を使用してサブプロセスを実行し、読み取られると、現在のRubyインタプリタまたはそのサブプロセスで実行されるコードの出力をストリーミングします。

コンテンツはセキュリティに敏感であるため、一時ファイルへの書き込みは理想的ではありません。

bash の同等のコード:

./run-program --password-file=<(printf '%s' "$password")

…実行すると、(Linux または /dev/fd/ を備えた別のプラットフォーム上で) 次のようなものが呼び出されます。

./run-program --password-file=/dev/fd/5

…ここで、 /dev/fd/5 を (ストリームなので 1 回だけ) 読み取って、 を展開した結果を取得できます。

Ruby で同じ呼び出し規約を実装するための最良のアプローチは何ですか?

解決策

IO.popen は、シェルの >() または <() と同じことを行います。つまり、読み書きできる名前のない一方向のパイプを作成します。

fd=IO.popen("echo hi") #"r"--reading by default
fd.read 
   #=>"hi\n"
   #Read from the file object
#OR access via the filesystem representation of the filedescriptor
File.read("/dev/fd/#{fd.fileno}")
   #This is just how linux works (note in Linux, each process sees `/dev/fd/` differently--it's actually the open filedescriptors a process has)

基本的に、これには名前のないパイプを使用するとよいでしょう。もう 1 つの一般的なパターンは、IO.pipe でパイプ ペアを作成し、子プロセスをフォークオフすることです。フォークするとファイル記述子が共有されるため、子プロセスと親プロセスは基本的にそのパイプ ペアを介して接続され、そのパイプ ペアは必要なだけ持つことができます。