9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

パイプを使った外部プログラムとの入出力

Posted at

外部プログラム(tcpdump)に標準入力から有限なデータを渡して,その標準出力を受け取りたい場面に出くわしたのだけれども,ライブラリ一発でうまくいかなかったので試行錯誤の末,それができる関数を作ってみた.

dump = File.read('tcpdump.dump')
filterd_dump = exec_stdio("/usr/sbin/tcpdump -r - -w - port 80 2>/dev/null", dump)

こんな感じで呼び出してデータを加工したい.

exec_stdio.rb
def exec_stdio cmd, input
	output = ''
	buf_size = 4096
	IO.popen(cmd, "r+b") do |io|
		offset = 0
		writable = true
		while offset < input.bytesize
			begin
				if writable
					chunk_size = [buf_size, input.bytesize-offset].min
					chunk = input.byteslice(offset, chunk_size)
					offset += io.write_nonblock(chunk)
				else
					output += io.read_nonblock(buf_size)
				end
			rescue IO::WaitReadable
				writable = true
				retry
			rescue IO::WaitWritable
				writable = false
				retry
			end
		end
		io.close_write
		while !io.eof
			begin
				output += io.read_nonblock(buf_size)
			rescue IO::WaitReadable
				retry
			end
		end
	end
	output
end

最初IO.writeで普通に書いていたらwiteでブロックされてプログラムがIO待ちで停止していた.調べたところパイプのバッファのサイズは65536(環境によっては4096)byteしか無く,どうもこのバッファを読み書き両方に使ってるみたい?なのでwrite_nonblockで適当な数だけ書いて,書き込めなくなったら例外をキャッチしてread_nonblockで読み出すということをやって,読み込めなくなったら今度は書き込むということをしている.書き込みが終わったらあとは読むだけなので,close_writeした後,read_nonblockで読み出している.本当にこんなコードで良いのだろうか?

バッククォートと一時ファイルで出来なくはないんだけど,わざわざディスクIOを発生させるのも嫌なので,直接データをやり取りしたかった.他にスマートな方法があったら教えて欲しい.

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?