< マイナー言語でお仕事 | らむ太語録 >
2010/12/14
車輪の再々発明 - dd wrapper in Gauche
ddを子プロセスで起動してシグナル送ってprogressを見ようという話。 現実的には、 スクリプト書くならわざわざdd起動せんでも自前でコピーすればいいんだけど、 子プロセス制御のサンプルとしては実に手頃な課題と思った。 出力のサイズを求めるget-totalは弾さんのに準拠。
#!/usr/bin/gosh (use srfi-1) (use gauche.process) (use text.progress) (use file.util) (define (main args) (let ([total (get-total (cdr args))] [proc (run-process `(dd ,@(cdr args)) :error :pipe)]) (sys-nanosleep #e5e8) (unwind-protect (doit proc total (make-text-progress-bar :header "" :header-width 0 :max-value total :num-width 20)) (process-wait proc)) (process-exit-status proc))) (define *sig* (if (#/linux/ (gauche-architecture)) SIGUSR1 29 #|SIGINFO|#)) (define (doit proc total bar) (let loop () (sys-nanosleep #e1e8) ;0.1s (process-send-signal proc *sig*) (rxmatch-case (read-line (process-output proc 'stderr)) [test eof-object?] [#/(\d+) bytes/ [_ c] (let1 cnt (x->integer c) (bar 'set cnt) (when (< cnt total) (loop)))] [else (loop)])) (bar 'finish)) (define (get-total args) (or (and-let* ([m (any #/if=(\S+)/ args)] [ (file-is-regular? (m 1)) ]) (file-size (m 1))) (let ([bs (any #/bs=(\d+)/ args)] [cnt (any #/count=(\d+)/ args)]) (* (or (and bs (x->integer (bs 1))) 256) (or (and cnt (x->integer (cnt 1))) 1)))))
cookbook的なポイントはこのあたりかな:
- アーキテクチャによる切り替え。
(define *sig* ...)
のところ。 - text.progressの使い方。
- 正規表現をpredicateとして使う。
(any #/if=(\S+)/ args)
とか。
泥縄的な話:
doitを呼び出す前に0.5秒のwaitを入れてるんだけど。 これを入れる前に、ofで指定するファイルが既に存在してかなり大きい場合に ddがコピーせずに終了してしまう (終了コードは、シグナル10を受けて終了) という現象が起きた。dd単体でstraceしてみると、出力ファイルのopenが 終わった後でシグナルハンドラを設定してるんで、もしかすると open(...|O_TRUNC) でファイルをtruncateしてる間にSIGUSR1を受けて 終了しちゃうのかもしれない。*BSDならSIGINFOがデフォルトで無視されるから 問題無いと思うが…
Tags: Programming, Gauche
Post a comment