保存する前のdiff(差分)をとる

前回までに、

と、diffを少しずつ拡張してきた。

今回さらに、

      • 保存する前の書類とのdiff(document_diff.sh)

をとってみたい。

通常、diffするときはファイルに保存されているモノ同士を対象にするのだけど、ちょっと工夫すると、クリップボード(command-Cでコピーした内容)とのdiffをとれる。

$ pbpaste|diff file1.txt -
  • pbpasteはクリップボードの内容を出力するOSX独自のコマンド。
  • それをパイプで繋いで、diffの第2引数に指定している。
  • diffコマンド末尾の - は、パイプで流れてきたテキストデータとみなされる。
  • ちなみに、- を第1引数の位置に変更すると、差分の計算が逆になる。
$ pbpaste|diff - file1.txt


よって、GUIエディタでfile1.txt編集中に全体をコピーして、ターミナルで上記コマンドを入力すれば、

      • 保存前の編集中の状態と、
      • 保存済のファイル内容の、

diffがとれるのだ!

GUI操作を追加する

  • いちいちすべてを選択して、コピーするのは面倒くさい。
  • AppleScriptならcommand-A、command-Cの操作ができる。
  • ところで、OSXにはosascriptコマンドが用意されている。
  • osascriptは、指定されたAppleScriptを実行するコマンド。
  • ならば、osascriptを利用して、AppleScriptでうまく包み込んであげれば、より使いやすいコマンドに仕上がるはず!
#!/bin/bash

open "$1"
sleep .5

osascript -e '
tell application "System Events"
keystroke "a" using command down -- command-a
keystroke "c" using command down -- command-c
delay .5
end tell'

pbpaste|diff -u "$1" -

osascript -e '
tell application "System Events"
keystroke tab using command down -- command-tab
end tell'

convert_diff.shを利用する

  • さらに、diffコマンドの部分を前回作ったconvert_diff.shに置き換えれば、バイナリファイルにも対応できるはず!
  • しかし、前回作ったconvert_diff.shは、ちょっとやっつけコード的なところがあって、そのままではdiffコマンドの置き換えには耐えられない。
  • できるだけ多くの状況でdiffコマンドの代わりに使えるように改良してみた。
#!/bin/bash

convert() {
  case $1 in
    *.scpt ) echo -n "osadecompile" ;;
    *.rtf )  echo -n "textutil -convert txt -stdout" ;;
    *.jpg )  echo -n "exiftool" ;;
    * )      echo -n "cat";;
  esac
}

OPTION=("$@")
PATH1=${OPTION[$(($# - 2))]}
PATH2=${OPTION[$(($# - 1))]}
unset OPTION[$(($# - 1))]
unset OPTION[$(($# - 2))]

# 一方がディレクトリだったら、他方のファイル名を補う
if [ -d "$PATH1" ] && [ -f "$PATH2" ]; then
  PATH1="$PATH1/`basename "$PATH2"`"
elif [ -f "$PATH1" ] && [ -d "$PATH2" ]; then
  PATH2="$PATH2/`basename "$PATH1"`"
fi

# 両方ともディレクトリだったら、変換せずにdiffを実行する
if [ -d "$PATH1" ] && [ -d "$PATH2" ]; then
  echo "\`diff" "$@""|colordiff'"
  diff "$@"|(`which colordiff`||`which cat`)
else
  echo "\`diff ${OPTION[@]} <(`convert "$PATH1"` \"$PATH1\") <(`convert "$PATH2"` \"$PATH2\")|colordiff'"
  diff "${OPTION[@]}" <(`convert "$PATH1"` "$PATH1") <(`convert "$PATH2"` "$PATH2")|(`which colordiff`||`which cat`)
fi
  • document_diff.shに組み込んでみた。
#!/bin/bash

open "$1"
sleep .5

osascript -e '
tell application "System Events"
keystroke "a" using command down -- command-a
keystroke "c" using command down -- command-c
delay .5
end tell'

pbpaste|~/convert_diff.sh -u "$1" -

osascript -e '
tell application "System Events"
keystroke tab using command down -- command-tab
end tell'

Lion以降での問題

  • 以上のシェルスクリプトは、Snow Leopard環境では何の問題もなく、うまく動いた。
  • Lion以降であっても、オートセーブとバージョンに対応していないアプリケーションでは問題なく動く。
  • しかし、LIon以降のオートセーブとバージョンに対応しているAppleScriptエディタやテキストエディットでは、

編集途中の差分がうまく表示されない...。

  • オートセーブとバージョンに対応の環境では、編集ウィンドウへの入力と同時に、保存ファイルにも変更が加えられてしまうよう仕様のようだ。
  • その結果、テキストファイルは編集した瞬間にファイルに保存されてしまう。
    • たとえ、システム環境設定 >> 一般 >>「書類を閉じるときに変更内容を保持するかどうかを確認」をチェック入りにした状態でも、編集した瞬間にファイルに保存されているのだ。
  • また、AppleScriptやリッチテキストも、編集中はファイルに何らかの変更が加えられる。
    • そのため、osascriptコマンドやtextutilコマンドで変換しようとしても、エラーが発生してテキストに変換できなくなってしまった...。

Mountain Lion環境のAppleScriptエディタでdiff-docが使えないのは、ちょっと残念。

エイリアス設定

  • シェルスクリプトも煮詰まってくると、常用したくなる。
  • その際、document_diff.shとか入力していると疲れる。
  • エイリアス設定しておくことにする。
$ alias diff-document ~/documen_diff.sh
$ alias diff-convert ~/convert_diff.sh
$ alias diff-dropbox ~/dropbox_diff.sh
  • diffコマンドのオプションン的な意味合いを込めて、-で接続してみたのだけど、さらに省略しても良いかもしれない。
$ alias diff-doc ~/documen_diff.sh
$ alias diff-conv ~/convert_diff.sh
$ alias diff-drop ~/dropbox_diff.sh