技術

ddコマンドのラッパー「pydd」をPythonで書いてみた

この記事に関連するかもしれない記事

ddコマンドのラッパー「ddr」をRubyで書いてみた
perl – 車輪の再発明 – ddコマンドのラッパーddp
(なんか弾さんのは、ifパラメータにサイズが取得できないファイル、例えば”/dev/urandom”とかを渡すと進捗の表示がおかしくなる)

触発されて書いてみた。

#!/usr/bin/env python

'''
dd command wrapper
'''

import sys, os, time
from subprocess import Popen, PIPE
from signal import SIGUSR1, SIGINFO
from select import select
import humansize

interval = 0.1

if 'linux' in os.uname()[0].lower():
	SIGPROGRESS = SIGUSR1
else:
	SIGPROGRESS = SIGINFO

if __name__ == '__main__':
	dd = Popen(['dd'] + sys.argv[1:], stderr = PIPE)
	while dd.poll() is None:
		dd.send_signal(SIGPROGRESS)
		time.sleep(interval)
		info = select((dd.stderr, ), (), (), 0)[0]
		if info:
			for i in xrange(3):
				line = info[0].readline()
				if 'bytes transferred' in line:
					b = int(line.split()[0])
					print '\t', humansize.approximate_size(b, False), '   \r',
					break
	print
	dd.communicate()
	sys.exit(dd.returncode)

Mac OS X + Python2.7でしかテストしてません。
進捗状況をパーセント表示するには、最終的なサイズを知る必要がありますが、最終的なサイズを知るためにはddのbs, countパラメータから計算した値とifパラメータで指定したファイルサイズのより小さい方を調べる必要があり、また想定しきれてない状況もあるかもしれず面倒なので、転送したデータサイズだけ表示するようにしてます。
humansizeだけ標準モジュールじゃないので、別途 http://diveintopython3-ja.rdy.jp/examples/humansize.py からダウンロードする必要があります。
転送したデータサイズを見やすくするためだけに使ってるので必要なければアレしてください。

今回ハマったところ
subprocess.Popenのstdoutとstderrを非同期で読み込む場合、例えばまだ3行しか出力されてないのに読み込みすぎる(今回の例ではreadメソッドとかreadlinesメソッドを使うだけで、またreadlineメソッドをEOFを検出するまでループで使うだけでそうなります)と、子プロセス(今回の場合はdd)が終わるまでそこでブロックしてしまい非同期でなくなってしまいます。
この問題を回避するため、selectを使い、未出力の状態のstderrをreadしてしまう事と、for i in xrange(3): の所で読み込みすぎる事を回避するようにしています。
しかしなんかもっといい方法があるんじゃあるまいか。

コメント

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください