less でいろんなファイルを syntax highlight して表示する

[lessとgrepに色を付けてコードを読みやすく @makisyu](http://qiita.com/makisyu/items/a8c4231a95d92f02b73a)
[lessコマンドを少し便利にしておく @hatchinee](http://qiita.com/hatchinee/items/586fb1c4915e2bb5c03b)

上記の記事を参考にして less に色付けて遊んでた。
んで初めて LESSOPEN を知ったのでぐぐったところ、lesspipe なるコマンドを発見。
これを LESSOPEN に書けばどうも圧縮ファイルなんかも less で見れるようになる模様（ただし中身がアーカイブの場合は一覧が出るだけ）。
早速組み込もう。

```
$ export LESSOPEN='| lesspipe %s | /usr/bin/src-hilite-lesspipe.sh'
```

まあこれが動かない。というのも、

0. lesspipe は普通のテキストファイルなどの場合は何も標準出力しないで正常終了する
0. src-hilite-lesspipe.sh はファイル名や拡張子を利用している
0. lesspipe で gz ファイルなどを展開しても、中身が標準出力されるだけなのでファイルの拡張子は不明

という問題があり、単純に pipe でつなげられない。
せっかくなので lesspipe と source-highlight の良いとこ取りをしようと思った。

以下、作ったもの。
まずはファイルのMIMEタイプを判定し、拡張子を標準出力するスクリプト。
file コマンドと Python の mimetypes モジュールのダブルチェックで精度高くなってるはず。

```en:/usr/local/bin/get_ext
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os.path
import sys
import mimetypes
import subprocess

argv = sys.argv
argc = len(argv)

if (argc != 2):
    sys.stderr.write("wrong argments: {0}\n".format(argv[0]))
    quit(1)

filename = argv[1]

# check MIME_TYPE by file command
filepath = os.path.abspath(filename)
cmd = "/usr/bin/file -ib {0}".format(filepath)
sp = subprocess.Popen(cmd,
                      stdout=subprocess.PIPE,
                      shell=True)
res = sp.stdout.readlines()
filetype = res[0].split(";")[0]


if not filetype:
    # check MIME_TYPE by mimetypes module
    filetype = mimetypes.guess_type(filename)[0]
    if not filetype:
        sys.stderr.write("MIME TYPE is None\n")
        quit(1)

fileext = mimetypes.guess_extension(filetype)
if fileext:
    print "{0}".format(fileext)
    quit(0)
else:
    sys.stderr.write("MIME TYPE is None\n")
    quit(1)
```

次に LESSOPEN で使うフィルタスクリプト。
lesspipe で標準出力されなかった場合は元のファイルをコピーして拡張子を補う（どんなファイルが渡されるかわからないから）。
標準出力された場合はファイルに書き出して拡張子を補う。
得られたファイルに対して source-highlight をかければ一丁あがり。
ちなみにこれは src-hilite-lesspipe.sh をベースに改変した。

```en:/usr/local/bin/my-lesspipe.sh
#! /bin/sh

for source in "$@"; do
    case $source in
    *ChangeLog|*changelog) 
        source-highlight -n --failsafe -f esc --lang-def=changelog.lang --style-file=esc.style -i "$source" ;;
    *Makefile|*makefile) 
        source-highlight -n --failsafe -f esc --lang-def=makefile.lang --style-file=esc.style -i "$source" ;;
    *)
        export BASENAME=`basename ${source}`
        export TEMPORARY_FILE="/tmp/${BASENAME}.$$.${BASENAME#*.}"
        lesspipe ${source} 2>&1 > ${TEMPORARY_FILE}

        if test ! -s ${TEMPORARY_FILE}; then
            cp ${source} ${TEMPORARY_FILE}
        fi

        FILEEXT=`get_ext ${TEMPORARY_FILE} 2>/dev/null`
        if test $? -eq 0; then
            mv ${TEMPORARY_FILE} ${TEMPORARY_FILE}.${FILEEXT}
            source-highlight -n --failsafe --infer-lang -f esc --style-file=esc.style -i ${TEMPORARY_FILE}.${FILEEXT}
            mv ${TEMPORARY_FILE}.${FILEEXT} ${TEMPORARY_FILE} 
        else
            source-highlight -n --failsafe --infer-lang -f esc --style-file=esc.style -i ${TEMPORARY_FILE}
        fi
        rm -f ${TEMPORARY_FILE};;
    esac
done
```

最後に環境変数を設定。LESS のオプションはお好みで。ただし"-R"だけ必須。

```en:~/.zshrc
export LESS='-g -j10 -R --no-init --quit-if-one-screen'
export LESSCHARSET=utf-8
export LESSOPEN='| /usr/local/bin/my-lesspipe.sh %s'
```

設定したら再度読み込ませる。

```
$ source ~/.zshrc
```

これで使用可能に。
less をかけると普通のテキストはもちろん、圧縮ファイルやアーカイブ、ディレクトリなどだいたいなんでもカラフルな表示が出ます。
ここまでやっておいてなんだけど、これってそこまでうれしいかな？わからない。
なお、ELF バイナリなんかはヘッダが出るけど、それ以外はぐじゃるので注意。
また、less に標準入力で渡したものはさすがにどうにもならないのであしからず。
あと、cp 使ってるので cp するとヤバいファイルには使わないでね（要改善）。