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): の所で読み込みすぎる事を回避するようにしています。
しかしなんかもっといい方法があるんじゃあるまいか。
コメント