web-dev-qa-db-ja.com

Grep:manページから見出しで単語を検索すると予期しない結果が発生する

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
}
19
ivan

その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 roffGROFF_SGR環境変数を設定した場合(またはコンパイル時にデフォルトがどのように設定されたかに応じてGROFF_NO_SGR変数を設定しない場合)次に、grotty-cオプションが渡されない限り)は、文字属性のBSトリックではなく、ANSI SGRターミナルエスケープシーケンスを使用します。lessは、-Rオプションで呼び出されたときにそれらを理解します。

FreeBSDの男は、MANCOLOR変数を設定してcoloursを要求しない限り、-cオプションを指定してgrottyを呼び出します(この場合、-cgrottyに渡されず、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 lMANPAGER='sed -n /NAME/l')にフィードすると、次のようになります。

\033[1mNAME\033[0m$

ここで、\e[1mはANSI互換端末で太字を有効にするシーケンスであり、\e[0mはすべてのSGR属性をデフォルトに戻すシーケンスです。

そのテキストでは、grep NAMEはそのテキストにNAMEが含まれているのと同じように機能しますが、テキストの一部のみが太字/下線になっているテキストを検索すると、問題が発生する可能性があります...

33

マニュアルページを見ると、ヘッダーが太字になっていることがわかります。これは、制御文字を使用してフォーマットすることによって実現されます。希望どおりにgrepを使用できるようにするには、これらを取り除く必要があります。

colユーティリティはこれに使用できます:

$ man bash | col -b | grep 'NAME'

-bオプションには、次の説明があります OpenBSDの場合

バックスペースを出力せず、各列位置に書き込まれた最後の文字のみを出力します。これは、mandoc(1)の出力の処理に役立ちます。


Linuxのcolマニュアル(Ubuntu)には最後の文がありません(ただし、同じように動作します)。

Linuxでは、MAN_KEEP_FORMATTING環境変数の設定を解除する(または空の文字列に設定する)と役立つ場合があり、grepの出力をcol -bに渡さなくてもmanを使用できるようになります。

13
Kusalananda