MacOSでmanページをgrepしようとすると、奇妙な動作が発生します。たとえば、Bashのマニュアルページには、文字列NAME
が明確に含まれています。
$ man bash | head -5 | tail -1
NAME
そして、name
に対してgrepを実行すると結果が得られますが、NAME
に対してgrepを実行すると、次の結果は得られません。
$ man bash | grep 'NAME'
$ man bash | grep NAME
私はそこにあることがわかっている他の大文字の単語を試してみました。Shell
を検索しても結果は得られませんが、BASH
を検索すると結果が得られます。
何が起きてる?
更新:すべての回答をありがとう!私がこれに遭遇したコンテキストを追加する価値があると思いました。 man
をラップするbash関数を書きたかったのですが、Shellビルトインのマニュアルページを検索しようとした場合は、Bashのマニュアルページの関連セクションにジャンプします。より良い方法があるかもしれませんが、ここに私が現在持っているものがあります:
man () {
case "$(type -t "$1")" in
builtin)
local pattern="^ *$1"
if bashdoc_match "$pattern \+[-[]"; then
command man bash | less --pattern="$pattern +[-[]"
Elif bashdoc_match "$pattern\b"; then
command man bash | less --pattern="$pattern[[:>:]]"
else
command man bash
fi
;;
keyword)
command man bash | less --hilite-search --pattern='^Shell GRAMMAR$'
;;
*)
command man "$@"
;;
esac
}
bashdoc_match() {
command man bash | col -b | grep -l "$1" > /dev/null
}
そのtail
コマンドに| sed -n l
を追加して、印刷できない文字を表示すると、おそらく次のようになります。
N\bNA\bAM\bME\bE
つまり、各文字はX
Backspace X
として書き込まれます。最近の端末では、文字はそのまま上書きされます(Backspace別名BS別名\b
別名^H
はカーソルを1列左に移動する文字です)。違いはありません。しかし、古代のテレタイプライターでは、インクが2倍になるため、文字が太字で表示されていました。
それでも、more
/less
のようなポケットベルはそのフォーマットを太字を意味すると理解しているので、roff
が太字のテキストを出力するために行っていることです。
一部の人の実装は、それらのシーケンスが使用されない方法でroff
を呼び出す(またはcol -b -p -x
実装の場合のように内部でman-db
を呼び出してそれらを削除する(MAN_KEEP_FORMATTING
環境変数がset))、出力がターミナルに送信されていないことを検出したときにポケットベルを呼び出さないでください(man bash | grep NAME
はそこで機能します)。
col -b
を使用して、これらのシーケンスを削除できます(下線には他のタイプ(_
BS X
)もあります)。
GNU roff
(GNUまたはFreeBSD)など)を使用しているシステムでは、-c -b -u
を確認することで、これらのシーケンスが最初から使用されるのを回避できます。オプションは、たとえば、-P-cbu
オプションがgrotty
に渡されるようにすることで、groff
に渡されます。
たとえば、以下を含むgroff
というラッパースクリプトを作成します。
#! /bin/sh -
exec /usr/bin/groff -P-cbu "$@"
$PATH
で/ usr/bin/groffの前に置くこと。
MacOSのman
(GNU roff
も使用)を使用すると、次のようにman-no-overstrike.conf
を作成できます。
NROFF /usr/bin/groff -mandoc -Tutf8 -P-cbu
そしてman
を次のように呼び出します:
man -C man-no-overstrike.conf bash | grep NAME
引き続きGNU roff
、GROFF_SGR
環境変数を設定した場合(またはコンパイル時にデフォルトがどのように設定されたかに応じてGROFF_NO_SGR
変数を設定しない場合)次に、grotty
(-c
オプションが渡されない限り)は、文字属性のBSトリックではなく、ANSI SGRターミナルエスケープシーケンスを使用します。less
は、-R
オプションで呼び出されたときにそれらを理解します。
FreeBSDの男は、MANCOLOR変数を設定してcoloursを要求しない限り、-c
オプションを指定してgrotty
を呼び出します(この場合、-c
はgrotty
に渡されず、grotty
は、 ANSI SGRエスケープシーケンスを使用するデフォルト)。
MANCOLOR=1 man bash | grep NAME
そこで働くでしょう。
Debianでは、GROFF_SGRはデフォルトではありません。もし、するなら:
GROFF_SGR=1 man bash | grep NAME
ただし、man
のstdoutは端末ではないため、GROFF_NO_SGR
変数をgrotty
にも渡す必要があります(col -bpx
を使用して、col
がBSシーケンスを削除できるようになっていると思いますMAN_KEEP_FORMATTING
をオーバーライドするGROFF_SGR
)を使用してSGRシーケンスを削除する方法代わりに行うことができます:
GROFF_SGR=1 MANPAGER='grep NAME' man bash
(端末で)SGRエスケープシーケンスを使用します。
そのとき、あなたはそれらのいくつか[〜#〜] name [〜#〜] sが端末上(およびless -R
ページャー内)に太字で表示されることに気付くでしょう。出力をsed -n l
(MANPAGER='sed -n /NAME/l'
)にフィードすると、次のようになります。
\033[1mNAME\033[0m$
ここで、\e[1m
はANSI互換端末で太字を有効にするシーケンスであり、\e[0m
はすべてのSGR属性をデフォルトに戻すシーケンスです。
そのテキストでは、grep NAME
はそのテキストにNAME
が含まれているのと同じように機能しますが、テキストの一部のみが太字/下線になっているテキストを検索すると、問題が発生する可能性があります...
マニュアルページを見ると、ヘッダーが太字になっていることがわかります。これは、制御文字を使用してフォーマットすることによって実現されます。希望どおりにgrep
を使用できるようにするには、これらを取り除く必要があります。
col
ユーティリティはこれに使用できます:
$ man bash | col -b | grep 'NAME'
-b
オプションには、次の説明があります OpenBSDの場合 :
バックスペースを出力せず、各列位置に書き込まれた最後の文字のみを出力します。これは、mandoc(1)の出力の処理に役立ちます。
Linuxのcol
マニュアル(Ubuntu)には最後の文がありません(ただし、同じように動作します)。
Linuxでは、MAN_KEEP_FORMATTING
環境変数の設定を解除する(または空の文字列に設定する)と役立つ場合があり、grep
の出力をcol -b
に渡さなくてもman
を使用できるようになります。