いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

less の環境変数 LESSOPEN で2つ以上のパイプを繋ぐ (nkfとsource-highlight同時指定)

概要

最近、less コマンドについて記事をちょこちょこ書いてます。

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

前回と前々回で、LESSOPENに nkf と source-highlight を指定する方法について書いたのですが、これを同時に設定してみたいってときもあります。ですが、ちょっとコツが必要なので、Tipsとしてメモしておきます。

そのままパイプを連結してみる

とりあえず、一旦そのままパイプを2つ繋いでみましょう。

lessで試してみるのは、EUC-JPなファイルとハローワールドなGoのファイル。

$ export LESSOPEN='| nkf -w %s | /usr/share/source-highlight/src-hilite-lesspipe.sh '

$ less -X -R hello.txt
"hello.txt" may be a binary file.  See it anyway? 
<A4><B3><A4><F3><A4>ˤ<C1><A4><EF><C0><A4><B3><A6>

<A4><B2><A4><B2><A1><BC>ۤۤ

<A5>ϥ���<A5><A5><EB><A5><C9>

あれ?ダメですね。。。nkf で変換出来ていない感じ。

helloworld.goの方も色付けされずに出力されちゃいます。

両方に %s つけてみる

source-highlightのシェルスクリプトはファイル名を指定しないとダメみたいなので、以下のように両方に %s つけちゃいます。

$ export LESSOPEN='| nkf -w %s | /usr/share/source-highlight/src-hilite-lesspipe.sh %s'

$ less -X -R hello.txt
Invalid LESSOPEN variable
"hello.txt" may be a binary file.  See it anyway? 

今度は Invalid LESSOPEN variable って表示されちゃいました。

LESSOPEN では %s は一回しか使えない

エラー内容がよくわからないので、とりあえず マニュアル を見ましょう。

$ man less

...割愛...

入力プリプロセッサ
       less のための「入力プリプロセッサ」を定義することができる。 
       less がファイルを開く前に、入力プリプロセッサで  入力ファイルの内容の表示の仕方を変更することができる。  
       入力プリプロセッサは、ファイルの内容を 代替ファイルと呼ばれる別ファイルに書き出す 単純な実行可能プログ
       ラム (もしくは、シェルスクリプト)  である。  代替ファイルの内容がオリジナルファイルの内容の代わりに表示される。  
       しかし、ユーザーにとってはオリジナルファイルが開かれているかのように見える。
       less は現在の代替ファイルの名前としてオリジナルファイルの名前を表示する。

       入力プリプロセッサは、ユーザーによって入力される オリジナルファイル名を 1 つのコマンドライン引き数として受け付ける。 
       そして、代替ファイルを生成し終えると、代替ファイル名を標準出力に表示する。 
       入力プリプロセッサが代替ファイル名を出力しない場合、 less は標準としてオリジナルファイルを用いる。  
       入力プリプロセッサは、標準入力を閲覧する場合には呼び出されない。  
       入力プリプロセッサを設定するためには、 入力プリプロセッサを呼び出すコマンドラインを環境変数 LESSOPEN に設定する。 
       このコマンドラインには、入力プリプロセッサコマンドが呼び出されるときに、 
       ファイル名に置き換えられる文字列 "%s"  を含んでいなければならない

...割愛...

ちゃんと、LESSOPENの挙動について書かれているのですが、長いしややこしいですねw

纏めると以下のことみたいです。

  • LESSOPEN指定している場合は、オリジナルのファイル名を %s で取れるよ
  • でも、%s は一つしか書けないよ
  • かつ、%s を含んでいないとダメだよ

なので、上の例みたいに %s を2つ書くと Invalid LESSOPEN variable ってエラーが出るってことですね。

で、どうする?

一回しか %s を使えない、かつ、今回の場合だと以下を実現しないといけません。

nkf で処理したデータを、source-highlight 側で利用して色づけする

なので、正攻法じゃ無理なので、一時的なファイルを作る作戦でいきます。

ただ、その場合でも面倒なことが一つあって

source-highlight さんは、拡張子で色付けのパターンを認識している

てことなので、元ファイルの拡張子を一時ファイルの方にも付ける必要がありますね。

で、長いですが以下のようにします。

export LESSOPEN='| target=%s; nkf -w ${target} > ${target}.less.tmp.${target##*.} | /usr/share/source-highlight/src-hilite-lesspipe.sh ${target}.less.tmp.${target##*.}'

一回しか %s を使えないので、最初に変数に入れちゃいます。

んで、nkf の結果を一時的なファイルに書き出して、それを source-highlight さんに食べてもらいます。

${target##*.} というのは、シェルの変数展開を利用してファイル名から拡張子を取得しています。

では、実行してみましょう。

$ less -X -R hello.txt 
こんにちわ世界

ほげほげー

ハローワールド


$ less -X -R helloworld.go 
package main

import (
        "fmt"
        "os"
        "strings"
)

func main() {
        os.Exit(run())
}

func run() int {
        fmt.Printf("%s\n", strings.ToUpper("hello"))
        fmt.Println("こんにちわ")
        return 0
}

オッケイですね。エンコーディングの変換も、色付けもされてます。

でも、一時ファイル残ったままですね。。

$ ls -1
hello.txt
hello.txt.less.tmp.txt
helloworld.go
helloworld.go.less.tmp.go

これはウザいので、なんとかしましょう。

LESSCLOSE環境変数を使う

LESSOPENは入力プリプロセッサなので、入力ポストプロセッサとしてLESSCLOSEという環境変数が用意されています。

これは、lessの処理が終わったときに利用される環境変数です。ここに後始末を書いておくと、終了時に勝手に呼んでくれます。

このLESSCLOSEに、LESSOPENで作った一時ファイルを消す処理を入れておきます。

export LESSCLOSE='target=%s; rm -f ${target}.less.tmp.${target##*.}'

では、再度やってみましょう。

$ ls -1
hello.txt
hello.txt.less.tmp.txt
helloworld.go
helloworld.go.less.tmp.go


$ less -X -R hello.txt
こんにちわ世界

ほげほげー

ハローワールド

$ less -X -R helloworld.go
package main

import (
        "fmt"
        "os"
        "strings"
)

func main() {
        os.Exit(run())
}

func run() int {
        fmt.Printf("%s\n", strings.ToUpper("hello"))
        fmt.Println("こんにちわ")
        return 0
}



$ ls -1
hello.txt
helloworld.go

ちゃんと一時ファイルが消えてくれてますね。

まとめ

以下のように設定するといい感じかもしれません。

export LESS=' -X -R '

export LESSOPEN='| target=%s; nkf -w ${target} > ${target}.less.tmp.${target##*.} | /usr/share/source-highlight/src-hilite-lesspipe.sh ${target}.less.tmp.${target##*.}'

export LESSCLOSE='target=%s; rm -f ${target}.less.tmp.${target##*.}'

過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com github.com

github.com