Kernel.openにパイプを渡す
Ruby2.4.3のリリースに、Net::FTPの脆弱性の修正をした、というのを見た。
CVE-2017-17405: Net::FTP におけるコマンドインジェクションの脆弱性について
ローカルのファイルを開くために、それぞれ内部で Kernel#open を使用しています。しかし、もし localfile 引数がパイプ文字 "|" で開始されていた場合、パイプ文字以降に並べられたコマンドが実行されてしまいます。
module function Kernel.#open (Ruby 2.4.0)
ファイル名 file が `|' で始まる時には続く文字列をコマンドとして起動し、コマンドの標準入出力に対してパイプラインを生成します
ファイル名が "|-" である時、open は Ruby の子プロセス を生成し、その子プロセスとの間のパイプ(IOオブジェクト)を返します。(このときの動作は、IO.popen と同じです。File.open にはパイプラインを生成する機能はありません)。
ファイル名の代わりに、 "|" を先頭につけたものを渡せば、パイプとかforkとかできるらしい。そういえばPerlでもこんなのあったわ。
やってみたメモ
パイプから読み取り
foo.txt
aaaaaa bbbbbb cccccc
pipe_r.rb
io = Kernel.open("| cat foo.txt") # 愚直に3回呼ぶ #puts io.gets #puts io.gets #puts io.gets # 一気読みする #puts io.readlines # 一行ずつ読む io.each do |line| puts line end io.close
$ ruby pipe_r.rb aaaaaa bbbbbb cccccc
パイプへ書き込み
pipe_w.rb
io = Kernel.open("| cat", "w") io.puts("Yeeeees!") io.close
$ ruby pipe_w.rb Yeeeees!
forkしてやりとり (IO.#popenのマニュアルのサンプルそのまま)
fork.rb
io = Kernel.open("|-", "r+") if io # parent io.puts "foo" puts io.gets io.close else # child s = gets print "child output: " + s exit end
$ ruby fork.rb child output: foo
使う機会があるかはわからないが、一応まとめてみた。