Personal tools
You are here: Home ブログ matsuyama less の意外な使い方
Document Actions

less の意外な使い方

会社のバイト君(兼、僕の先生)と一緒に less って無駄に高機能だよねという話が出たので紹介。僕は最近まで知らなかったのですが、意外に知ってる人多いかも?

一般的にページャーとして用いられている less ですが、実は以下のような使い方ができたりします。

% less /bin/sh
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x805bdd0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          654192 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         27
  Section header string table index: 26
...

見ての通り、 /usr/sh をバイナリとして less するのではなく、 /usr/shobjdump の結果を less しています。また、それに加えて、 .so.tar.gz 、``.ps`` 、 .pdf.png.mp3 などさまざまな種類のファイルを求められるであろう情報に変換したものを less することができます。

それで、実際どうなっているのかというと、 less がファイルを開くときに LESSOPEN 環境変数を読みこんで、その値をフィルタプログラムとして起動することで less する内容を変化させられるようになっています。

僕の環境では以下のようになっています。

% echo $LESSOPEN
|lesspipe.sh %s
% type lesspipe.sh
/usr/bin/lesspipe.sh

/usr/bin/lesspipe.sh:

#!/bin/bash
#
# Preprocessor for 'less'. Used when this environment variable is set:
# LESSOPEN="|lesspipe.sh %s"

# TODO: handle compressed files better

trap 'exit 0' PIPE

guesscompress() {
       case "$1" in
               *.gz)  echo "gunzip -c" ;;
               *.bz2) echo "bunzip2 -c" ;;
               *.Z)   echo "compress -d" ;;
               *)     echo "cat" ;;
       esac
}

lesspipe_file() {
       local out=$(file -L -- "$1")
       case ${out} in
               *" ar archive"*)    lesspipe "$1" ".a" ;;
               *" tar archive"*)   lesspipe "$1" ".tar" ;;
               *" CAB-Installer"*) lesspipe "$1" ".cab" ;;
               *" troff "*)        lesspipe "$1" ".man" ;;
               *" shared object"*) lesspipe "$1" ".so" ;;
               *" Zip archive"*)   lesspipe "$1" ".zip" ;;
               *" LHa"*archive*)   lesspipe "$1" ".lha" ;;
               *" ELF "*)          readelf -a -- "$1" ;;
               *": data")          hexdump -C -- "$1" ;;
               *)                  return 1 ;;
       esac
       return 0
}

lesspipe() {
       local match=$2
       [[ -z ${match} ]] && match=$1

       local DECOMPRESSOR=$(guesscompress "$match")

       case "$match" in

       ### Doc files ###
       *.[0-9n]|*.man|\
       *.[0-9n].bz2|*.man.bz2|\
       *.[0-9n].gz|*.man.gz|\
       *.[0-9][a-z].gz|*.[0-9][a-z].gz)
               local out=$(${DECOMPRESSOR} -- "$1" | file -)
               case ${out} in
                       *troff*)
                               # Need to make sure we pass path to man or it will try 
                               # to locate "$1" in the man search paths
                               if [[ $1 == /* ]] ; then
                                       man -- "$1"
                               else
                                       man -- "./$1"
                               fi
                               ;;
                       *text*)
                               ${DECOMPRESSOR} -- "$1"
                               ;;
                       *)
                               # We could have matched a library (libc.so.6), so let
                               # `file` figure out what the hell this thing is
                               lesspipe_file "$1"
                               ;;
               esac
               ;;
       *.dvi)      dvi2tty "$1" ;;
       *.ps|*.pdf) ps2ascii "$1" || pstotext "$1" || pdftotext "$1" ;;
       *.doc)      antiword "$1" || catdoc "$1" ;;
       *.rtf)      unrtf --nopict --text "$1" ;;

       ### URLs ###
       ftp://*|http://*|*.htm|*.html)
               for b in links2 links lynx ; do
                       ${b} -dump "$1" && exit 0
               done
               html2text -style pretty "$1"
               ;;

       ### Tar files ###
       *.tar)                  tar tvvf "$1" ;;
       *.tar.bz2|*.tbz2|*.tbz) tar tjvvf "$1" ;;
       *.tar.gz|*.tgz|*.tar.z) tar tzvvf "$1" ;;

       ### Misc archives ###
       *.bz2)        bzip2 -dc -- "$1" ;;
       *.gz|*.z)     gzip -dc -- "$1"  ;;
       *.zip)        unzip -l "$1" ;;
       *.rpm)        rpm -qpivl --changelog -- "$1" ;;
       *.cpi|*.cpio) cpio -itv < "$1" ;;
       *.ace)        unace l "$1" ;;
       *.arc)        arc v "$1" ;;
       *.arj)        unarj l -- "$1" ;;
       *.cab)        cabextract -l -- "$1" ;;
       *.lha|*.lzh)  lha v "$1" ;;
       *.zoo)        zoo -list "$1" ;;
       *.7z)         7z l -- "$1" ;;
       *.a)          ar tv "$1" ;;
       *.so)         readelf -h -d -s -- "$1" ;;
       *.mo|*.gmo)   msgunfmt -- "$1" ;;

       *.rar|.r[0-9][0-9])  unrar l -- "$1" ;;

       *.deb|*.udeb)
               if type -p dpkg > /dev/null ; then
                       dpkg --info "$1"
                       dpkg --contents "$1"
               else
                       ar tv "$1"
                       ar p "$1" data.tar.gz | tar tzvvf -
               fi
               ;;

       ### Media ###
       *.bmp|*.gif|*.jpeg|*.jpg|*.pcd|*.pcx|*.png|*.ppm|*.tga|*.tiff|*.tif)
               identify "$1" || file -L -- "$1"
               ;;
       *.avi|*.mpeg|*.mpg|*.mov|*.qt|*.wmv|*.asf|*.rm|*.ram)
               midentify "$1" || file -L -- "$1"
               ;;
       *.mp3)        mp3info "$1" || id3info "$1" ;;
       *.ogg)        ogginfo "$1" ;;
       *.flac)       metaflac --list "$1" ;;
       *.iso)        isoinfo -d -i "$1" ; isoinfo -l -i "$1" ;;
       *.bin|*.cue)  cd-info --no-header --no-device-info "$1" ;;

       ### Source code ###
       *.awk|*.groff|*.java|*.js|*.m4|*.php|*.pl|*.pm|*.pod|*.sh|\
       *.ad[asb]|*.asm|*.inc|*.[ch]|*.[ch]pp|*.[ch]xx|*.cc|*.hh|\
       *.lsp|*.l|*.pas|*.p|*.xml|*.xps|*.xsl|*.axp|*.ppd|*.pov|\
       *.diff|*.patch|*.py|*.rb|*.sql|*.ebuild|*.eclass)

               # Allow people to flip color off if they dont want it
               case ${LESSCOLOR} in
                       [yY][eE][sS]|1|true) LESSCOLOR=1;;
                       [nN][oO]|0|false)    LESSCOLOR=0;;
                       *)                   LESSCOLOR=1;; # default to colorize
               esac
               [[ ${LESSCOLORIZER+set} != "set" ]] && LESSCOLORIZER=code2color
               if [[ ${LESSCOLOR} == "0" ]] || [[ -z ${LESSCOLORIZER} ]] ; then
                       # let less itself handle these files
                       exit 0
               fi

               # Only colorize if we know less will handle raw codes
               for opt in ${LESS} ; do
                       if [[ ${opt} == "-r" || ${opt} == "-R" ]] ; then
                               ${LESSCOLORIZER} "$1"
                               break
                       fi
               done
               ;;

# May not be such a good idea :)
#      ### Device nodes ###
#      /dev/[hs]d[a-z]*)
#              fdisk -l "${1:0:8}"
#              [[ $1 == *hd* ]] && hdparm -I "${1:0:8}"
#              ;;

       ### Everything else ###
       *)
               # Sanity check
               [[ ${recur} == 2 ]] && exit 0

               # Maybe we didn't match due to case issues ...
               if [[ ${recur} == 0 ]] ; then
                       recur=1
                       lesspipe "$1" "$(echo $1 | tr '[:upper:]' '[:lower:]')"

               # Maybe we didn't match because the file is named weird ...
               else
                       recur=2
                       lesspipe_file "$1"
               fi

               exit 0
               ;;
       esac
}

if [[ -z $1 ]] ; then
       echo "Usage: lesspipe.sh <file>"
elif [[ $1 == "-V" ]] ; then
       Id="cvsid"
       cvsid="$Id: lesspipe.sh,v 1.16 2006/07/30 17:36:01 vapier Exp $"
       cat <<-EOF
               $cvsid
               Copyright 2001-2005 Gentoo Foundation
               Mike Frysinger <vapier@gentoo.org>
                    (with plenty of ideas stolen from other projects/distros)
               
               
       EOF
       less -V
elif [[ $1 == "-h" || $1 == "--help" ]] ; then
       cat <<-EOF
               lesspipe.sh: preproccess files before sending them to less
               
               Usage: lesspipe.sh <file>
               
               lesspipe.sh specific settings:
                 LESSCOLOR env     - toggle colorizing of output
                 LESSCOLORIZER env - program used to colorize output (default: code2color)
               
               Run 'less --help' or 'man less' for more info
       EOF
elif [[ -d $1 ]] ; then
       ls -alF -- "$1"
else
       recur=0
       lesspipe "$1" 2> /dev/null
fi

/usr/bin/lesspipe.sh は Gentoo の中の人が書いた物らしいのですが、 file などを駆使していて、かなり多くの形式を認識できるようになっています( html の取得は wget だろ普通…)。画像ファイルは画像ファイル情報じゃなくて AA 化したものを表示したものを less したいとかいう場合は、 AA 化プログラムを拾ってくるなり作るなりして

*.bmp|*.gif|*.jpeg|*.jpg|*.pcd|*.pcx|*.png|*.ppm|*.tga|*.tiff|*.tif)
        identify "$1" || file -L -- "$1"
        ;;

*.bmp|*.gif|*.jpeg|*.jpg|*.pcd|*.pcx|*.png|*.ppm|*.tga|*.tiff|*.tif)
        iamge2aa "$1" || file -L -- "$1"
        ;;

とでもやれば動くでしょう。

でまあ、件のバイト君とも話していたのですが、これはやりすぎだろうと思うのです。バイナリなら cat -v ぐらいが適切なんじゃないのかと。それよか lv 使っている現状を可及的に解決すべきでしょう。

ちなみに GNU less のソース読んでみましたが、なんとも言えないものでした。

Category(s)
linux
The URL to Trackback this entry is:
http://dev.ariel-networks.com/Members/matsuyama/less-functions/tbping
Add comment

You can add a comment by filling out the form below. Plain text formatting.

(Required)
(Required)
(Required)
(Required)
(Required)
This helps us prevent automated spamming.
Captcha Image


Copyright(C) 2001 - 2006 Ariel Networks, Inc. All rights reserved.