カタカタブログ

SIerで働くITエンジニアがカタカタした記録を残す技術ブログ。Java, Oracle Database, Linuxが中心です。たまに数学やデータ分析なども。

lessでzipファイルの中身が見えるわけ 〜LESSOPENとlesspipe.sh〜

前回、zipファイルを展開せず中身を見るにはlessコマンドを使うのがよいという記事を書いたが、そもそもなぜlessでzipファイルの中が見れるのか調べてみた。
※前回記事: Linuxでzipファイルの中身を展開せずに見るにはlessがおすすめ - カタカタブログ

lessコマンドの仕組み

どうやら、LESSOPENという環境変数にlesspipe.shというシェルスクリプトが定義されており、これがless実行対象のファイルに対して事前に処理が入る。

$ env | grep LESS
LESSOPEN=||/usr/bin/lesspipe.sh %s

つまり、targetというファイルに対してlessコマンドを打つ場合、

$ less target

以下のような処理が内部で行われているようなイメージ。

$ lesspipe.sh target | less

そして、lesspipe.shの中では引数で渡されたファイルの拡張子をチェックし、拡張子ごとにテキスト表示しやすい形式に変換する処理をかませている。例えば、zipやjarの場合は内部でzipinfoコマンドを発行しており、その結果をlessに渡していてくれている。前回の記事ではzipとjarは見れるがwarやearは見れないと書いたが、それはこれが原因だった。

lesspipe.shのzipの部分一部抜粋

case "$1" in
(・・・略)
*.zip|*.jar|*.nbm) zipinfo -- "$1" ;;

例えば、zipの場合、

$ less target.zip

というコマンドは、

$ zipinfo target.zip | less

のような動きとなり、zipの中身をlessで見れるという仕組みになっている。

ちなみに、当環境のlesspipe.shのロジックは以下のようになっており、zip以外にも様々な形式に対応しているよう。

lesspipe.shの一部抜粋

case "$1" in
*.[1-9n].bz2|*.[1-9]x.bz2|*.man.bz2|*.[1-9n].[gx]z|*.[1-9]x.[gx]z|*.man.[gx]z|*.[1-9n].
lzma|*.[1-9]x.lzma|*.man.lzma)
        case "$1" in
        *.gz)           DECOMPRESSOR="gzip -dc" ;;
        *.bz2)          DECOMPRESSOR="bzip2 -dc" ;;
        *.xz|*.lzma)    DECOMPRESSOR="xz -dc" ;;
        esac
        if [ -n "$DECOMPRESSOR" ] && $DECOMPRESSOR -- "$1" | file - | grep -q troff; then
                $DECOMPRESSOR -- "$1" | groff -Tascii -mandoc -
                exit $?
        fi ;;&
*.[1-9n]|*.[1-9]x|*.man)
        if file "$1" | grep -q troff; then
                man -l "$1" | cat -s
                exit $?
        fi ;;&
*.tar) tar tvvf "$1" ;;
*.tgz|*.tar.gz|*.tar.[zZ]) tar tzvvf "$1" ;;
*.tar.xz) tar Jtvvf "$1" ;;
*.xz|*.lzma) xz -dc -- "$1" ;;
*.tar.bz2|*.tbz2) bzip2 -dc -- "$1" | tar tvvf - ;;
*.[zZ]|*.gz) gzip -dc -- "$1" ;;
*.bz2) bzip2 -dc -- "$1" ;;
*.zip|*.jar|*.nbm) zipinfo -- "$1" ;;
*.rpm) rpm -qpivl --changelog -- "$1" ;;
*.cpi|*.cpio) cpio -itv < "$1" ;;
*.gpg) gpg -d "$1" ;;
*.gif|*.jpeg|*.jpg|*.pcd|*.png|*.tga|*.tiff|*.tif)

lesspipe.shの検証

ところで、環境変数LESSOPENの設定やlesspipe.shの実装は環境によって異なるようだが、これはつまりlesspipe.shの実装を好きにカスタマイズできるということだろうか。ということで、今回はCent OS 7.0で簡単な独自lesspipe.shを読ませてlessコマンドの処理が変わるか試してみた。
まずOSとlessのバージョンを示しておく。

$ cat /etc/redhat-release
CentOS Linux release 7.0.1406 (Core)
$ less --version
less 458 (POSIX regular expressions)
Copyright (C) 1984-2012 Mark Nudelman

less comes with NO WARRANTY, to the extent permitted by law.
For information about the terms of redistribution,
see the file named README in the less distribution.
Homepage: http://www.greenwoodsoftware.com/less

さて、当環境ではデフォルトの状態で環境変数LESSOPENにはlesspipe.shが入っている。%sの部分に対象のファイルが置換されるよう。

$ env | grep LESS
LESSOPEN=||/usr/bin/lesspipe.sh %s

LESSOPENの動きを確認するため、独自のシェルスクリプトに変更してみる。

$ export LESSOPEN="||/tmp/mylesspipe.sh %s"

独自のシェルスクリプトの中身は、受け取った引数をcat -nするだけ。つまり、行番号つきで第一引数のパスを標準出力するというもの。

$ cat /tmp/mylesspipe.sh
cat -n $1
$ chmod +x /tmp/mylesspipe.sh

この状態でlessコマンドを実行してみると、cat -nに対してページャが動いたような実行結果になる。

$ less test.txt
     1  1234
     2  abcd
     3  XYZ
test.txt (END)

このことから、LESSOPENで指定されたカスタムのシェルスクリプトが呼ばれ、その出力がlessされていることが分かる。

ということは、話を元のlesspipe.shに戻すと、以下のようにスクリプトを編集し、warやearのときもzipinfoをかますように変えてやれば、lessで中身が見れるようになる!

case "$1" in
(・・・略)
*.zip|*.jar|*.nbm|*.war|*.ear) zipinfo -- "$1" ;;

まとめ

lessが実際に実行されるとき、LESSOPENという環境変数に定義されたlesspipe.shスクリプトによる処理がかんでいることがわかった。また、このスクリプト自体は様々な形式のファイルに対して「lessしやすいよう」工夫されており、zipがlessでみれるのもその実装の一つにすぎないということだった。さらにlesspipe.shをカスタマイズすれば、より使いやすいオリジナルlessが実現できるかもしれない!

以上

関連記事