残り時間... ならぬ終了予定時刻を出力するシェルスクリプト

私のシェルスクリプトの書く傾向として、先にフォルダを作成して、残りの処理数をフォルダ数で示す物が多い。 残りのフォルダ数の増減によって、残り時間を出力するプログレスバーがあれば良いな。と思ったのだが、なかったので作る事にした。

パイプ等で現在のカウントを出力する物ではなく、argvにワンライナーシェルを入力し、そこから0までの時間を表示するものにした。

./progress.sh 10s "find folder -mindepth 2 -type d | wc -l"

こんな感じで、処理できる物を作ろうと考えた。

でできた物がこちら。

#!/bin/bash


if [ $# -ne 2 ]; then
        echo "$0 10s \"find . -type f | wc -l\"" 1>&2
        echo "9999...0 countdown progress" 1>&2
        echo "sleep 10s; command" 1>&2
        echo "bash -c \"find . -type d | wc -l\" == num .. countdown you must this 0" 1>&2
        exit 1
fi

START_TIME=$(date +%s);
START_COUNT=$(bash -c "$2");

clear;

while [ ! $(bash -c "$2") -eq 0 ]; do

    # now
    count=$(bash -c "$2");
    time=$(date +%s);

    # percentage
    # 0% to not work.
    if [ $START_COUNT -eq $count ]; then
        percent=0;
        remain_time=0;
    else
        total_end_count=$(($START_COUNT - $count));
        percent=$(echo "scale=5; $total_end_count / $START_COUNT * 100" | bc);
        remain_time=$(echo "scale=5; ($time - $START_TIME) / $total_end_count * $count" | bc -l);
        remain_sec=$(echo $remain_time | sed s/\.[0-9,]*$//g);
    fi

    # remaining time



    echo "$percent% $remain_time s $(date --date='TZ="Asia/Tokyo" '$remain_sec' sec')";
    sleep $1;
done

残り秒数まで出力できたが、残り秒数を時間やday表示にするより、dateコマンドの+秒後の方が楽にできたので、終了予定時刻を出力した。

f:id:s3ij1nn:20200208135130p:plain

右下の画面が実行画面。

大体、残り時間が6日かかるとわかった。

xargs と parallel の使い分けについて

xargs と parallel の使い分けについて

xargsを使わずにparallelを使う理由

parallel を使う理由の一つに、xargsのプログレス状態を確認したいから。なんて理由が多いと思う。 もちろん xargs の -tオプションをいれて、xargsが何を走らせてるのか確認するというのもあるが、-verboseオプションみたいなもので、プログレスが使えるparallelを..と考える。

parallel がxargsより遅い理由

xargs と parallelの大きな違いは、xargs は コマンドを直接実行してくれるのに対して、parallelはchshのデフォルトのシェルを起動してから、そのシェル上で動かす。 つまり、以下のxargsとparallelは同じ意味になる。

xargs -I% bash -c "ls %";
parallel -I% ls %

それで、parallelを使うとオーバーヘッドが大きいなんて言われてるが、関数書いて実行させる場合は同じになる。

#!/bin/bash -l

hello(){
  echo "hello $1";
}

export -f hello;

cat name.txt | xargs -I% bash -c "hello %";

cat name.txt | parallel -I% hello %;

また、parallel ではメモリやCPUのスレッド数に応じて自動的にプロセスを管理してくれる機能がある。

なんでこんな事書いたかって

2-3時間程度の処理なら、xargsの方が早いが、2-3日レベルの処理の場合、parallelの方が早いて事に気づいた。

wget で content-Lengthとか status codeをgrepする

わかってるんだ curl の方が優れてるのは

curl はHTTP/2に対応し、絶賛時代遅れ感が否めないwget

そんなcurlに唯一対抗できる素晴らしい機能が、フォルダを指定してダウンロードできる-Pオプションと、ファイルスキップ機能-nc

いや、まあ if [ ! -f file]; then curl -o file; fi;すれば良いだけの話だし、file=$path/$(basename url);すれば良いやんけ、と思う方も居るかと思うが、xargs でワンライナーすると結構きつい。

ここ最近、自分の5年以上使ってきたプログラミング言語を半年以上使わずシェルスクリプトだけで毎日送ってる。xargsとparallelが便利なんや。研究の為に書いたら、すっげぇ汚いコードになってて、諦めてシェルスクリプトに戻った。

画像破損チェックでImageMagickのidentify使うのはよろしくない

そんで、200万ファイル以上の画像をクローンしてこいってなった時、wgetでガンガン落としてると画像が破損してる事が多い。そこで、ImageMagickidentifyを使うことにした。

apple.stackexchange.com

実際に使ってみた結果、画像サーバがエラー吐いてHTMLとか出力すると、画像でないファイルが出来上がる。 画像でないファイルをidentifyに読み込ませると、stderrに出力しやがる。

identify-im6.q16: insufficient image data in file `1049768/file.jpg' @ error/jpeg.c/ReadJPEGImage/1039.

つまり以下のコードは画像じゃないファイルだと動かない

find . -type f | xargs -I@ -n1 bash -c "if identify @ | grep Corrupt; then rm @ fi";

で画像でも、wgetの途中で接続が切れた画像ファイルでも反応しなかった。!?

本題のwget で content-Lengthとか status codeをgrepする

じゃあ、wget の レスポンスヘッダのContent-Lengthで確認しよう。というのが本題。

wget -q --server-response --spider URLするとstderrに出力される !? なぜだ*

なのでstderrをpipeに送る為に 2>&1を入れる

あとはレスポンスヘッダが200なのと、Content-Lengthが入ってるのを確認する。 ついでに下4行をwhileの中にブチ込んでも良い。

response="$(wget -q --server-response --spider URL 2>&1 )";
# " "外すと改行が消されます
if test $(echo $response | grep -Eq 'HTTP[^ ]+ 200') -a $(echo $response | grep -q 'Content-Length'); then
  echo $(echo $response | grep -oE 'Content-Length: [0-9]+' | grep -oE '[0-9]+');
fi

結果は 11451みたいな自然数が出力される

*なぜだ
後日かんがえてたら、答えがでた。
wget で保存しながら、サーバステータスをパイプできる。
wget -q --server-response -O test.zip http://localhost/test.zip 2>&1 | grep -oE 'Content-Length: [0-9]+' | grep -oE '[0-9]+' | xargs -n1 -I% bash -c "if [ $(ls -la test.zip | gawk -F' ' '{print $5}') = % ]; then echo 'download size match!'; fi" 汚いが、ワンライナーでダウンロードが途中で止まってダウンロードに失敗しているか確認できる。

OpenDriveを6年近く利用し続けて、諦めた

容量無制限のオンラインストレージという言葉に惹かれて、OpenDriveを契約し5年と9ヶ月。値下げあり、突然の倍額への値上げありと振り回されて来た。しかし、ここまで容量無制限のオンラインストレージという看板を初期の頃から続けて来たストレージも珍しい。

容量無制限

私が最初に見かけた容量無制限のサービスは、無料のレンタルウェブホスティングだ。ウェブサイトを沢山抱えておけば、ウェブサイト運営者に有料サービスへの誘導がやり易く、ウェブサイト管理者を多く集める目玉として無料かつ容量無制限のレンタルホスティングを目玉とした。

次に来たのが、アップローダアップローダではダウンロード者に課金を求める事で、ファイルのアップロードを無制限にする事ができた。

その次にようやく来るのが、オンラインストレージだった。結局 Bitcasa, Amazon Drive Unlimitedと破れ去っていった。

OpenDriveは過去より使いやすくなったが

OpenDrive、過去は酷かった。アップロードに失敗する、公式アプリケーションとブラウザでの1ファイル目のみ速度が早いが、それ以降は速度が規制される。公式アプリケーションを使えばいいのだが、公式アプリケーションが不安定(当時まだlibfuseとdokanが不安定だった)で仮想ドライブ上で大きいファイルを開くのは、再起動を賭けた賭けになっていた。

当時からAPIにはmd5によるチェックサム出力機能があったのだが、公式アプリケーションは対応しておらず、公式アプリケーションでアップロードしても破損している事が多々あった。ドラック&ドロップによるアップロードを使用すると10kb/sまで速度が落ち、500MB以上になると数日かかると計算され、キャンセルするとOSが数時間フリーズすることになった。

ともかく、昔のOpenDriveはアップロード機能に関しては酷かった。

大容量ファイルのオンラインストレージ間処理の神ツール rcloneの登場

なんでこんなに酷いのに使い続けたと言えば、数ファイルダウンロードして資料を扱い、バックアップする分には使えるが、保管したデータを丸ごとダウンロードして何処かに移すのは難しかった。

そんな中、公式でさえ使っていなかったチェックハッシュを使ってアップロードダウンロードできるツールが登場。rcloneだ。

ただ、OpenDriveの仕様上、大きいファイルのダウンロードをAPIから利用しようとするとダウンロードに失敗する。(手作業でブラウザからダウンロードすることはできる)

お亡くなりになったファイル

10月ぐらいから、別のオンラインストレージに移行してるのだがなんと、1割のファイルが死んでいるのだ。破損ではない、一般にも公開してもいない大学のレポート等がアクセス不能になっている。酷いのが503(一時的にアクセス不能)と返してくる。amazonのGlacier等に保管してるのであれば、一ヶ月もあれば復活するだろうと考えていたが、復活しない。つまり、復元不可能になっている。

これからOpenDriveを利用しようと考えている人達へ

バックアップ目的で使用しないで欲しい。私は酷い目にあった。

-- 追記

Windows版の仮想フォルダは日本人によってメンテされてるdokan (fuseをwinAPIで動かせるようにしたもの)は結構ちゃんと動きます。fuse公式のツールが重い以外には。Mac版のfuseはosxfuseとmacfuseの2大台頭があり様々な開発者もどっちつかずな上、Appleが深いAPIを触らせるのを嫌っていた為、フリーズすると数時間ぐらい復帰出来ませんでした。 ただ、WindowsでOpenDriveを触った総合時間が1日程度なので、当時のWindows版はわかりません。ただ、"ドラック&ドロップによるアップロードを使用すると10kb/sまで速度が落ちる"のと、長時間使用してるだけで軽いフリーズするのは確認しました。(でもMacよりは軽かったです)

Comskip に生成されるvdrの時間計算をshell scriptでやる方法

comskip については以下を参照

www.jifu-labo.net

ここからの自動化処理の殆どが、perlだったりrubyだったり、はたまたWindows Batchだったり

Winユーザなら便利すぎる素晴らしいソフトウェアがあったり

github.com

そんなの無視してshell scriptで処理しようぜって話ですが、時間計算が結構めんどくさい

0:00:00.00 start
0:00:55.21 end
0:08:42.22 start
0:10:13.01 end
0:20:07.08 start
0:21:36.00 end
0:28:26.00 start
0:29:55.15 end

こんなのがあった場合、以下のように切り出したいわけです。

ffmpeg -i input.ts -ss 0:00:55.21 -t  82.88 -c copy output_1.ts
ffmpeg -i input.ts -ss 0:10:13.01 -t 632.03 -c copy output_2.ts
ffmpeg -i input.ts -ss 0:21:36.00 -t 649.28 -c copy output_3.ts
ffmpeg -i "concat:output_1.ts|output_2.ts|output_3.ts|output_4.ts" -c copy output.ts

while か for でいけそうですが、-t オプションが曲者な訳です そこで以下のように計算しました

t=$(echo "scale=2; `date '+%s.%2N' -d '0:00:55.21'` - `date '+%s.%2N' -d '0:08:42.22'`" | bc);

解説

date '+%s' -d '0:00:55.21'

で秒数変換されます。しかし、ffmpeg の -t オプションでは SS.MS で表記してるので

date '+%s.%2N' -d '0:00:55.21'

で SS.MS時間に変換ができます。このまま計算して

$((date '+%s.%2N' -d '0:00:55.21' - `date '+%s.%2N' -d '0:08:42.22'`))

といきたい所ですが、デフォルトのbashスクリプトでは小数点以下を含んだ計算に対応していません。そこでbcを使います。

echo "3.22 - 2.11" | bc

のような使い方をします。よって以下のようになる訳です。

echo "scale=2; `date '+%s.%2N' -d '0:00:55.21'` - `date '+%s.%2N' -d '0:08:42.22'`" | bc