cdif / sdif / watchdiff now runs on Linux

OS X でしか使っていなかったのですが、Linux で実行してみたら使えなかったので修正しました。

  • 端末の幅を取得するのに stty を使っていたが、仕様の違いがあるので tput を使うように変更
  • Text::Glob は標準モジュールだと思っていたが違ったので使わないように変更

また、一時ファイルの取り扱いが変わっています。

  • 従来は /tmp にファイルを作成して使用後に削除していた
  • それを IO::File::new_tmpfile を使うように変更
    • これだとファイルをオープンすると同時に unlink するので削除する必要がない
    • そのため異常終了してもファイルが残らない
  • diff コマンドに与えるパスは /dev/fd/ を使うように変更

追記

会社の FreeBSD で試してみると /dev/fd/3, /dev/fd/4 が開けないといいますね。 CLOSE ON EXEC の解除ができていないのでは疑ったのですが、そうではなくてファイルシステムの問題のようです。 マニュアルを見ると devfs(5) は 0, 1, 2 しか提供しないので、fdescfs(5) を使えと書いてあります。 こういう場合は設定を変えてくださいという方針でいいんでしょうかね。

watchdiff -- cdif wrapper for monitoring command result

最近、gitbub で iij/watch というコマンドがひっそりと公開されました。 管理者の yasuoka 君は以前の同僚で、このコマンドは2000年に一緒の部署で働いていた時にみんなで作ったものですね。 元々は BSD/OS に watch というコマンドがあって、ちょっと拡張しようといじってたら結局作り直しちゃったやつです。 ずっと社内で使っていたものを公開したのには何か理由があるようですが、詳しいことは知りません。

このコマンドの機能は、指定したコマンドを定期的(デフォルトでは2秒おき)に実行して結果を画面に出すだけです。 少し工夫してあって、変更された部分を反転できるようになってたり、 出力が画面に入り切らない時には、スクロールして見ることもできます。

たいしたものではないけど意外と便利で、今でもたまに使ったりします。 たとえば、こんな感じ。

  • WiFi の電波が取れないなー、なんでかな〜てな時

    watch ifconfig -a

  • やっべー、ディスクぎりぎりだよ、足りるかあ〜てな時

    watch df

  • ネットワーク調子悪いなあ、なんか起こってるのかしらん?てな時

    watch netstat -s

まあ、これでもいいんだけど、反転だけではどうも色気がないんですね。 ncurses は色にも対応しているようなので、そういうの使うという手もあるのだけど、変更部分を調べる仕組みが単純に作ってあるので、行が増えたり減ったりすると途端にお手上げなわけです。

だったら、せっかく cdif で色が出るようにしたんだから、それを使えばいいじゃんということで作ってみました。 こんな感じの画面が2秒おきに表示されると考えておくんなさい。 cdif の変更点は、単に余計な部分を表示しないようにしただけです。 あ、実装言語は Perl ですよ。

f:id:uta46:20140215223419p:plain

似たようなツールはほかにもたくさんありそうですけどね。 watchdiff は、デフォルトでは垂れ流しモードなので、画面を書き換えずにスクロールしていきます。 最近はマルチウィンドウ環境が前提なので、その方が案外使いやすい場面も多いのではないかと思います。

しかし、en0 と連動する p2p0 ってインタフェースは一体何なんでしょうか? などという今まで気にもしなかかったことも、ビジュアル化することで気がついたりするわけです。 どうも、AirDrop と関係があるくさいです。

ちなみに watch コマンドで同じような画面を見ると、こうなります。 これでは、どこが変わったのかさっぱりわかりません。 ubuntu にも watch コマンドがあるらしいですが、それだとどう見えるのか気になるところです。

f:id:uta46:20140215232114p:plain

即席なので実装は粗粗、マニュアルテキトー。 特に端末制御周りは手抜きで、OS X 以外で動くかどうかはかなり怪しいです。 何かあったら github の issues でもなんでも適当にどうぞ。

NAME

watchdiff - repeat command and watch the differences

SYNOPSIS

watchdiff option -- command

Options:

    --refresh=#     refresh screen count (default 0)
    --interval=#    interval time between execution in second (default 2)
    --count=#       command repeat count (default 1000)
    --[no]date      show date at the beginning (default on)
    --[no]silent    do not show same result (default off)
    --[no]newline   print newline after command result (default on)
    --exec          set executing commands
    --diff=command  diff command used to compare result

Example:

    watchdiff df

    watchdiff --silent df

    watchdiff --refresh 5 --noclear -- df

    watchdiff --refresh 1 -- netstat -s -p ip

    watchdiff --refresh 1 --exec uptime --exec iostat --exec df

    watchdiff -s --refresh 1 --diff 'sdif --cdif -U100' -- netstat -sp ip

    watchdiff --nodate --nonewline --count=18 --interval=10 date

DESCRIPTION

Please install cdif(1) command as a default backend.

AUTHOR

Kazumasa Utashiro

https://github.com/kaz-utashiro/watchdiff

SEE ALSO

diff(1), cdif(1), sdif(1)

COPYRIGHT

Use and redistribution for ANY PURPOSE are granted as long as all copyright notices are retained. Redistribution with modification is allowed provided that you make your modified version obviously distinguishable from the original one. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED.

ANSI 端末の216色カラーテーブル

あ、それから、なんとなく sdif--colortable というオプションが入ってます。 こんなのね。

最初の列は横縞、2番目の列は縦縞に見えます。 3番目はどっちかなあという感じ。

RGB の値から輝度を計算するのには次の式を使います。 明るさに与える影響は、緑が6割もあるので、緑の変化がはっきり見えるんですね。

輝度 = 0.298912 * R + 0.586611 * G + 0.114478 * B

f:id:uta46:20140201133622p:plain

cdif v2.9 / sdif v2.6 Latin and Russian support

ラテン系の言語とロシア語に対応しました。 タイ語も処理は一応入れてみたけど、ちゃんと表示できません。 例文は質問サイトから拝借。

漢字の連続をひとつの単語として処理するのはやめました。 これで中国語もデフォルトのままで問題ありません。 日本語については若干振る舞いが変わりますが、それほど問題ではないと思うし、ちゃんと見たい時には --mecab オプションを使った方がいいです。

それから、変更部分の背景色を変更しました。 実際に使ってみると、左右が同じ色の方が比較しやすいのと、シアンやマジェンタに赤や青を乗せると判別しにくいからです。

Français / French

f:id:uta46:20140201125506p:plain

Deutsch / German

f:id:uta46:20140201125617p:plain

Русский / Russian

f:id:uta46:20140201125638p:plain

cdif/sdif でいろんな言語を試してみた / multi language trial

Korean

多分大丈夫だろうと思ってはいたが、韓国語はなんの問題もなく処理できる。

As expected, Korean is fine.

f:id:uta46:20140131140507p:plain

Chinese

中国語は、案の定漢字の連続を一単語として処理すると塩梅が悪い。 -B オプションをつけるとうまく行っているような気がする。

Chinese works ok with -B option for cdif.

f:id:uta46:20140131140630p:plain

Arabic

よもやと思ってアラビア語をやってみた。

Arabic doesn't work.

f:id:uta46:20140131140858p:plain

やっぱり駄目だったか...

そもそも、どうしてアラビア語の部分だけではなくて全体が左右反転してしまうのかとか、まったくメカニズムがわからない。

ちなみに、アラビア語はサンプルがみつからなかったので、雪国の冒頭部分の日本語と英訳を Google で翻訳したものである。

cdif/sdif 256色対応

cdif/sdif で ANSI の256色指定ができるようにしてみました。 デフォルトを換えちゃいましたよ。 実際には、6x6x6 の216色です。 うまい指定形式を思いつかないので、24段階のグレースケールには未対応です。 いい考えがあったら教えてください。

ANSI 256 color support

  • Actually 216 colors (6x6x6)
  • Format
    • Foreground / Background
  • Color
    • 000 .. 555
    • 000000 .. FFFFFF

f:id:uta46:20140127131846p:plain

sdif updated

Side-by-side / ANSI color / word context

f:id:uta46:20140124233940p:plain

Unicode / Japanese

f:id:uta46:20140124234055p:plain

With --mecab morphology

f:id:uta46:20140124234120p:plain

NAME

sdif - side-by-side diff viewer for ANSI terminal

SYNOPSIS

sdif file_1 file_2

diff ... | sdif

--number, -n        print line number
--digit=n           set the line number digits (default 4)
--truncate, -t      truncate long line
--onword            fold line on word boundaries
--width, -w #       specify width of output (default is 80)
--diff=s            set diff command
--diffopts=s        set diff command options
--[no]color         use color or not
--colormap, --cm    specify color map
--mark=position     mark position (right, left, center, side) or no
--man               display manual page
-c, -C#             context diff
-u, -U#             unified diff

--cdif              use ``cdif'' as word context diff backend
--cdifopts          set cdif command options
--mecab             pass --mecab option to cdif

DESCRIPTION

sdif is inspired by System V sdiff(1) command. The basic feature of sdif is making a side-by-side listing of two different files. All contents of two files are listed on left and right sides. Center column is used to indicate how different those lines. No mark means no difference. Added, deleted and modified lines are marked with `<' and `>' character.

1 deleted  <
2 same          1 same
3 changed  <>   2 modified
4 same          3 same
            >   4 added

It also reads and formats the output from diff command from standard input. Besides normal diff output, context diff -c and unified diff -u output will be handled properly.

Each lines can be displayed in different colors. Read --colormap section in ths manual for detail.

While sdif doesn't care about the contents of each modified lines, it can read the output from cdif command which show the word context differences of each lines. Option --cdif set the appropriate options for cdif. Set --nocc, --nomc options at least when invoking cdif manually. Option --notc is pereferable because text color can be handled by sdif.

Environment valuable SDIFOPTS is used to set default options.

OPTIONS

  • --w=width, -w

    Use width as a width of output listing. Default width is 80. If the standard error is assinged to a terminal, the width is taken from it if possible.

  • --number, -n

    Print line number on each lines.

  • --digit=n

    Line number is diplayed in 4 digits by dafult. Use this option to change it.

  • -c, -Cn, -u, -Un

    Passed through to the back-end diff command. Sdif can interpret the output from normal, context (diff -c) and unified diff (diff -u).

  • --truncate, -t

    Truncate lines if they are longer than printing width.

  • --onword

    Fold longs line at word boundaries.

  • --cdif

    Use cdif command instead of normal diff command.

  • --cdifopts=option

    Specify options for backend cdif command.

  • --mecab

    Pass --mecab option to backend cdif command. Use --cdifopts to set other options.

  • --diff=command

    Any command can be specified as a diff command to be used. Piping output to sdif is easier unless you want to get whole text.

  • --diffopts=option

    Specify options for backend diff command.

  • --mark=position

    Specify the position for mark. Choose from left, right, center, side or no. Default is center.

  • --[no]color

    Use ANSI color escape sequence for output. Default is true.

  • --colormap=colormap, --cm=colormap

    Basic colormap format is :

      FIELD=COLOR
    

    where the FIELD is one from these :

      OLD       NEW       UNCHANGED
      --------- --------- ---------
      OCOMMAND  NCOMMAND           : Command line
      OFILE     NFILE              : File name
      OMARK     NMARK     UMARK    : Mark
      OLINE     NLINE     ULINE    : Line number
      OTEXT     NTEXT     UTEXT    : Text
    

    You can make multiple filelds same color joining them by = :

      FIELD1=FIELD2=...=COLOR
    

    Also wildcard can be used for field name :

      *CHANGE=BDw
    

    Multiple fields can be specified by repeating options

      --cm FILED1=COLOR1 --cm FIELD2=COLOR2 ...
    

    or combined with comma (,) :

      --cm FILED1=COLOR1,FIELD2=COLOR2, ...
    

    COLOR is combination of single character representing uppercase foreground color :

      R  Red
      G  Green
      B  Blue
      C  Cyan
      M  Magenta
      Y  Yellow
      K  Black
      W  White
    

    and corresponding lowercase background color :

      r, g, b, c, m, y, k, w
    

    and other effects :

      S  Standout (reverse video)
      U  Underline
      D  Double-struck (boldface)
      F  Flash (blink)
      E  Expand
    

    E is effective for command, file and text line. That line will be expanded to window width filling up by space characters. Left column is expanded always. You may want to use this to set background color for right column.

    Defaults are :

      OCOMMAND => "CSE"
      NCOMMAND => "MSE"
      OFILE    => "CDE"
      NFILE    => "MDE"
      OMARK    => "Cw"
      NMARK    => "Mw"
      UMARK    => "w"
      OLINE    => "Y"
      NLINE    => "Y"
      ULINE    => "Y"
      OTEXT    => "C"
      NTEXT    => "M"
      UTEXT    => ""
    

    This is equivalent to :

      sdif --cm 'OCOMMAND=CSE,NCOMMAND=MSE,OFILE=CDE,NFILE=MDE' \
           --cm 'OMARK=Cw,NMARK=Mw,UMARK=w' \
           --cm '*LINE=Y,OTEXT=C,NTEXT=M,UTEXT='
    

    Try next setting if you want to make modified section more visible.

      sdif -n --cdif --cm '[ON]TEXT=wE,UMARK=,OMARK=C,NMARK=M'
    

AUTHOR

Kazumasa Utashiro

https://github.com/kaz-utashiro/

SEE ALSO

perl(1), diff(1), cdif(1)

cdif, sdif