そうだ!Dropboxのdiff(差分)をとろう!
そもそも前回なぜ、Dropboxへcurlコマンドでアクセスしたかったのか?というと、Dropboxの過去のバージョンとの差分を見たかったのだ。Dropboxの素晴らしすぎる機能の一つに、過去一ヶ月間くらいのファイルの変更履歴を保持する仕組みがある。過去のバージョンを見たいときは...
- FinderでDropbox内のファイルを右クリックして「以前のバージョンを表示」を選択する。
- すると、WebブラウザのDropboxページが開いて、バージョン履歴がリスト表示されるのだ。
- そのリンクをクリックすれば、その内容が表示される。
- そのリンクをoption-クリックすれば、ファイルとしてダウンロードされる。
素晴らしい!AppleがOSXでバージョンという仕組みを作る以前からバージョン管理されていた。ファイルの同期も信頼性が高く、使い勝手もすこぶる快適。常にiDiskの一歩先を進んでいた。そんなDropboxで、diffをとりたくなったのだ。
先人の技を探す
- 自分がやりたいと思ったことは、世界のどこかで、誰かがきっと試している。
- 車輪の再発明をしないように、まずはWebを念入りに検索するべきなのだ。
- そのようにして、二つの仕組みを見つけた。
DropDiff(コマンド)
$ chmod a+x ~/Downloads/dropdiff.sh $ ~/Downloads/dropdiff.sh ~/Dropbox/hello.txt Unhandled DB schema version 2
- 実行権限を付加して、いくつかのバージョンを試してみたが、エラーが出てしまう。
- どうもDropboxのDBを参照してパスを取得しているようなのだけど、それがうまくできていない感じ。
- また、たとえ正常に実行できたとしても、最新ファイルと一つ前のバージョンの差分に限定されるようだ。
dropbox_diffプロジェクトの開始
というわけで、自分で作ってみる気になって、前回につながる。(そんな経緯でcurlでDropboxにアクセスしたくなったのだ)
方針など
- シンプルに、diffコマンドの出力で十分。
- dropdiff.shを参考にしようとしたが、diff本来のコードより、周辺のコードが自分にとっては複雑過ぎ。
- よって、全部自分で書き直す。
- 最新バージョンだけでなく、あらゆるバージョンの差分をとれるようにする。
前回のコード
#!/bin/bash EMAIL_ADRESS=$1 PASSWORD=$2 FILE_NAME=$3 curl -L -c cookie.txt -o output.html https://www.dropbox.com/login TOKEN=`cat output.html | grep -e '<input type="hidden" name="t" value=".*" />' | grep -o 'value=".*"' | grep -o '".*"' | grep -o '[^"].*[^"]'` curl -L -b cookie.txt -c login_cookie.txt -o output.html \ --data-urlencode "t=$TOKEN" \ --data-urlencode "login_email=$EMAIL_ADRESS" \ --data-urlencode "login_password=$PASSWORD" \ https://www.dropbox.com/login curl -L -b login_cookie.txt -o output.html "https://www.dropbox.com/revisions/$FILE_NAME"
最新のdiffのみ出力できるバージョン
- 最初はとにかく動くことを目指して、最新バージョンとその一つ前のdiffのみを実現する。
- 前回、ログインするところまではできているので、
- あとはバージョン履歴のページを出力して、
- その中から、各バージョンへのリンクURLを抜き出し、
- 最新バージョンの一つ前のファイルをダウンロードして、
- diff出力している。
- コマンド引数にパスワードなどを入力するのはセキュリティ上よろしくないので、(コマンド履歴に残ってしまう)
- コマンド実行後に対話的にメールアドレスとパスワードを入力するように変更した。
#!/bin/bash FILE_PATH=$1 FILE_NAME="${FILE_PATH##*/}" REVISION_FILE_PATH="https://www.dropbox.com/revisions/$FILE_NAME" read -p 'email-adress: ' EMAIL_ADRESS read -s -p 'password: ' PASSWORD; echo curl -L -c cookie.txt -o output.html https://www.dropbox.com/login TOKEN=`cat output.html | grep -e '<input type="hidden" name="t" value=".*" />' | grep -o 'value=".*"' | grep -o '".*"' | grep -o '[^"].*[^"]'` curl -L -b cookie.txt -c login_cookie.txt -o output.html \ --data-urlencode "t=$TOKEN" \ --data-urlencode "login_email=$EMAIL_ADRESS" \ --data-urlencode "login_password=$PASSWORD" \ https://www.dropbox.com/login curl -L -w "%{url_effective}" -b login_cookie.txt -o output.html $REVISION_FILE_PATH echo URLS=(`cat output.html|grep -o '<a href="https://dl-web.dropbox.com/get/.*</a>'|grep -o '"https://.*"'|grep -o '[^"].*[^"]'`) echo ${URLS[@]} CONTENTS=`curl -s -b login_cookie.txt ${URLS[1]}` echo -en $CONTENTS|diff -u - $FILE_PATH
- 実行結果
$ ./dropbox_curl.sh ~/Dropbox/hello.txt email-adress: xxxx@mail.com password: % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 19952 0 19952 0 0 14062 0 --:--:-- 0:00:01 --:--:-- 21639 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 369 0 274 100 95 293 101 --:--:-- --:--:-- --:--:-- 340 100 100k 0 100k 0 0 45941 0 --:--:-- 0:00:02 --:--:-- 163k % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 24212 0 24212 0 0 20839 0 --:--:-- 0:00:01 --:--:-- 23529 https://www.dropbox.com/revisions/hello.txt https://dl-web.dropbox.com/get/hello.txt?w=507561d6&sjid=29 https://dl-web.dropbox.com/get/hello.txt?w=507561d6&sjid=28 --- - 2012-10-07 17:12:28.000000000 +0900 +++ /Users/zari/Dropbox/hello.txt 2012-10-06 13:06:31.000000000 +0900 @@ -1 +1 @@ -hello +hello world!!
以上が必要最小限のコードのべた書き
無駄な処理、繰り返しを削除する
- login_cookie.txtを手に入れれば、それを手土産にDropboxに自由にアクセスできる。
- よって、毎回ログインするのは無駄なので、必要なときだけログインする仕組みに変更。
- 具体的には、バージョン履歴のページにアクセスしたとき、リダイレクトされたらログインし直すようにした。
- (ログインが必要な時は、ログインページへリダイレクトするはず)
- -w "%{url_effective}" = 最後にアクセスしたURLを返すオプション設定。
- -Lと組み合わせることで、リダイレクト先のURLが取得できるのだ。
- 呪文のような長いコードは、機能別に関数にまとめた。
#!/bin/bash FILE_PATH=$1 FILE_NAME="${FILE_PATH##*/}" REVISION_FILE_PATH="https://www.dropbox.com/revisions/$FILE_NAME" dropbox_login() { read -p 'email-adress: ' EMAIL_ADRESS read -s -p 'password: ' PASSWORD; echo curl -L -c cookie.txt -o output.html https://www.dropbox.com/login TOKEN=`cat output.html | grep -e '<input type="hidden" name="t" value=".*" />' | grep -o 'value=".*"' | grep -o '".*"' | grep -o '[^"].*[^"]'` curl -L -b cookie.txt -c login_cookie.txt -o output.html \ --data-urlencode "t=$TOKEN" \ --data-urlencode "login_email=$EMAIL_ADRESS" \ --data-urlencode "login_password=$PASSWORD" \ https://www.dropbox.com/login } revision_files_page() { curl -L -w "%{url_effective}" -b login_cookie.txt -o output.html $REVISION_FILE_PATH } extract_file_urls() { cat output.html|grep -o '<a href="https://dl-web.dropbox.com/get/.*</a>'|grep -o '"https://.*"'|grep -o '[^"].*[^"]' } download_revision_file() { curl -s -b login_cookie.txt ${URLS[$1]} } # リダイレクトした時だけログインし直す RES=`revision_files_page` if [ $RES != $REVISION_FILE_PATH ]; then dropbox_login revision_files_page fi echo URLS=(`extract_file_urls`) echo ${URLS[@]} CONTENTS=`download_revision_file 1` echo -en $CONTENTS|diff -u - $FILE_PATH
任意のバージョンを指定してdiff
- 上記までは、最新のバージョンとその一つ前の差分しかとれなかった。
- ここでは対話的に操作して、任意のバージョンを指定できるようにした。
- whileブロック内が対話的に任意のバージョンを指定する部分。
- 何も入力しない場合は、最新バージョンとその一つ前のdiff。
- バージョン番号を一つだけ入力した場合は、バージョン番号とその一つ前のdiff。
- バージョン番号をスペースで区切って二つ入力した場合は、二つのバージョン番号のdiff。
- バージョン番号の範囲をチェックしたり、補助コマンドの処理などで長くなっているが、やっていることは必死に条件判断をしているだけ。
#!/bin/bash FILE_PATH=$1 DROPBOX_PATH="${FILE_PATH##*/Dropbox/}" REVISION_FILE_URL="https://www.dropbox.com/revisions/$DROPBOX_PATH" # Dropboxへログインする dropbox_login() { read -p 'email-adress: ' EMAIL_ADRESS read -s -p 'password: ' PASSWORD; echo curl -L -c cookie.txt -o output.html https://www.dropbox.com/login TOKEN=`cat output.html | grep -e '<input type="hidden" name="t" value=".*" />' | grep -o 'value=".*"' | grep -o '".*"' | grep -o '[^"].*[^"]'` curl -L -b cookie.txt -c login_cookie.txt -o output.html \ --data-urlencode "t=$TOKEN" \ --data-urlencode "login_email=$EMAIL_ADRESS" \ --data-urlencode "login_password=$PASSWORD" \ https://www.dropbox.com/login } # ファイルのバージョン管理のページを取得する revision_files_page() { curl -L -w "%{url_effective}" -b login_cookie.txt -o output.html $REVISION_FILE_URL } # バージョンごとのファイルのURLを抜き出す extract_file_urls() { cat output.html|grep -o '<a href="https://dl-web.dropbox.com/get/.*</a>'|grep -o '"https://.*"'|grep -o '[^"].*[^"]' } # 指定したバージョンのファイルをダウンロードする download_revision_file() { curl -s -b login_cookie.txt ${URLS[$(($MAX_VERSION - $1))]} } # リダイレクトした時だけログインし直す RES=`revision_files_page` if [ $RES != $REVISION_FILE_URL ]; then dropbox_login revision_files_page fi # ファイルのURLを配列にして、バージョンの個数を取得する URLS=(`extract_file_urls`) MAX_VERSION=${#URLS[@]} # 対話的に操作する while : do # バージョンリストを表示 echo echo '*** Version list(Top is newest) ***' for (( i = $MAX_VERSION; i > 0 ; --i )) do echo -e " $i: Version$i" done # 入力待ち read -p 'Select( number [o]pen [h]elp [q]uit )> ' VER1 VER2 # 入力コマンドの処理、バージョン番号を取得、入力値のエラー処理 if [[ $VER1 = "q" ]]; then echo 'quit' exit elif [[ $VER1 = "o" ]]; then echo 'open' continue elif [[ $VER1 = "h" ]]; then echo 'help' continue elif [[ $VER1 =~ ^[0-9]*$ ]] && [[ $VER2 =~ ^[0-9]*$ ]]; then if [[ -z "$VER1" ]]; then VER1=$MAX_VERSION fi if [[ -z "$VER2" ]]; then VER2=$VER1 VER1=`expr $VER1 - 1` fi if [[ $VER1 -gt 0 ]] && [[ $VER1 -le $MAX_VERSION ]] && [[ $VER2 -gt 0 ]] && [[ $VER2 -le $MAX_VERSION ]]; then break fi fi echo 'error!' exit done echo "\`diff Version$VER1 Version$VER2\`" echo # 指定バージョンをダウンロードする if [[ $(($MAX_VERSION - $VER1)) = 0 ]]; then CONTENTS_1=`cat $FILE_PATH` else CONTENTS_1=`download_revision_file $VER1` fi if [[ $(($MAX_VERSION - $VER2)) = 0 ]]; then CONTENTS_2=`cat $FILE_PATH` else CONTENTS_2=`download_revision_file $VER2` fi # diff出力 diff -u <(echo "$CONTENTS_1") <(echo "$CONTENTS_2") echo
仕上げ
対話的な操作のopenコマンド・helpコマンドの処理を追加して、ひとまず完了。
- コードはgithubにアップロードしてみた。
- http://github.com/zarigani/dropbox_diff
開発環境
参考ページ
久しぶりにシェルスクリプトを触ると、毎度のことながら、呪文のように感じてしまう。これらのページに出逢えなかったら、100万年かかってもdropbox_diff.shは完成しなかったでしょう。すべての素晴らしい情報ページに感謝です!