意図的にフォーマットを維持しながら、マニュアルページをページングなしでパイプするにはどうすればよいですか?

意図的にフォーマットを維持しながら、マニュアルページをページングなしでパイプするにはどうすればよいですか?

最小限の例:

man git | cat

実際の例:

man git | grep --color=always -C 3 "pathspec"

--pager="cat"FWIW、さまざまなポケットベルを使用し、さまざまなポケットベルにパイピングを試みました。私もvimcat同じ方法で試しましたが、残念ながら凍った。私も役に立たunbufferedなかった。

-Tたぶんorを使うことは可能かもしれませんが、{g,n,t}roffどうすればいいのかわかりません。less/ vimetcにパイプしてstdoutに渡す方法はありますか?書式指定(色など)そしてページ付けなし




編集する:以下のStéphaneの助けのおかげで、一致するすべてのマニュアルページを簡単に検索して強調表示するための素晴らしい方法がありました。これは私にとってゲームチェンジャーです。ありがとう、ステファン!

ここに画像の説明を入力してください。

興味のある方のために、スクリプトの主な部分は次のとおりです。 (間違いなく改善することができます。フィードバックはいつでも歓迎します。)

編集する:今Stéphaneからの追加のフィードバックを統合しました。途方もない進展!

編集する:Ubuntu 20.04.4 LTSおよびmacOS 12.6で動作するように、このソリューションをさらに改善しました。パイプでul

#!/bin/bash
arg_search="$1"

typeset -A seen=()
while IFS= read -ru3 filename; do
    if (( ! seen[\$filename]++ )); then
        # 'man -awK' sometimes returns bogus results due to searching through
        # everything, including comments in the man source file, so filter
        # these out. Note that this creates a secondary problem: legitimate
        # results may get filtered out when the search term is two or more
        # words spanning across lines. This can be solved with a multiline
        # regex using `pcre2grep -M 'search\s+term`, but this is perhaps
        # growing beyond the scope of things here.
        { man --no-hyphenation --no-justification --regex -- "$filename" | \
            grep -iqE -- "$arg_search"; } || continue

        # Print filename. Fill terminal width with highlighted background color.
        filename_bar="$(printf '%s\t' "$filename" | expand -t $(tput cols))"
        printf '\e[1;103;30m%s\e[0m\n' "$filename_bar"
        # Per Stéphane's feedback, here's a cleaner version of the same for zsh:
        # psvar=${(mr[COLUMNS])filename} print -P '%K{yellow}%F{black}%1v%k%f'

        MAN_KEEP_FORMATTING=1 man \
            --no-hyphenation --no-justification --regex -- "$filename" | \
            ul | \
            grep -iE --color=always -- "$arg_search"
    fi
    # 'unbuffer' seems to be required for macOS in order to unbuffer the output,
    # as 'stdbuf -oL -eL' doesn't seem to work. Install on macOS using
    # 'brew install expect' [sic], which contains unbuffer. This otherwise
    # doesn't hurt anything on Ubuntu 20.04.4 LTS. Unbuffering the output of
    # 'man -awK' is important because it can take a long time to run.
done 3< <(unbuffer man -awK --regex -- "${arg_search}")

答え1

努力する:

GROFF_SGR=1 man -P 'grep --color=always -C 3 pathspec'  git

としてパイプする場合(少なくともDebianベースのシステムでは)、従来の文字列の代わりに太字/下線にANSI SGRエスケープシーケンス(たとえば)を使用してgrepを困難にすることができますgrep。バラよりGROFF_SGR=1\e[1mBOLD\e[mB\bBO\bOL\bLD\bDGrep:マニュアルページでタイトル内の単語を検索すると予期しない結果が発生するもっと学ぶ。

ポケットベル(ここ)は、grep標準出力が端末に行くときにのみ実行されることに注意してください。manこのようなコマンドをパイピングすると、コマンドとlessフォーマットgrepが無効になります。

MAN_KEEP_FORMATTING私が答えたとき、テストでSGRを無効にするために使用された設定がありました。Grep:マニュアルページでタイトル内の単語を検索すると予期しない結果が発生するその時はそうでしたが、もはやそうではありません(少なくともUbuntu 20.04ではそうではありません)。

したがって、ポケットベルを使用するには、次のようにします。

GROFF_SGR=1 MAN_KEEP_FORMATTING=1 man git |
  grep --color=always -C 3 pathspec | less -R

によって開始されませんがmanless-Pgreppager

たとえば、次のようになりますzsh

mangrep()
  GROFF_SGR=1 CODE="
     grep --color=always -C3 ${(j[ ])${(qq)@[2,-1]}} | pager
  " man -P 'sh -c "eval \"$CODE\""' $1

たとえば、次のようになります。

mangrep git -i pathspecs

あなたpagerless起動したので、次のようにman設定されたLESS*環境変数を継承しますman

LESS=-ix8RmPm Manual page git(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$PM Manual page git(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$
LESSCHARSET=utf-8

bash(4.4+)に対応する内容は次のとおりです。

mangrep() {
  local page=$1 IFS=' '
  shift
  GROFF_SGR=1 CODE="
    grep --color=always -C3 ${*@Q} | pager
  " man -P 'bash -c "eval \"$CODE\""' ${page:+"$page"}
}

(空白で連結する代わりに"$*"(sh互換の引用)の代わりに(デコードが必要です)を使用し、配列の範囲と組み合わせることができないため、最初の引数を使用して保存します。)IFS=' 'j[ ]@Qbash(qq)shift$page@Q

許可されるオプションman(から):zsh

mangrep() {
  local page=$@[(i)[^-]*]
  GROFF_SGR=1 CODE="
     grep --color=always -C3 ${(j[ ])${(qq)@[page+1,-1]}} | pager
  " man -P 'sh -c "eval \"$CODE\""' "$@[1,page]"
}

たとえば、次のようになります。

mangrep -a printf -i precision

すべてのマニュアルページ(配信済み)で大文字と小文字を区別しない(配信済み)を見つけますprecisioni-igrepaprintf-aman

関数の代わりにスクリプトで作成してください。

#! /bin/zsh -
page=$@[(i)[^-]*]
GROFF_SGR=1 CODE="
  grep --color=always -C3 ${(j[ ])${(qq)@[page+1,-1]}} | pager
  " exec man -P 'sh -c "eval \"$CODE\""' "$@[1,page]"

それからzsh


編集内容に投稿したコードに関するいくつかのコメント:

[...]

man -w -a -K "$arg_search" | \

でなければなりませんman -w -a -K -- "$arg_search"。それ以外の場合は、で始まる項目を検索できません(回避策などの項目を-使用することを除く)。arg_search='[-]-foo'

    stdbuf -oL -eL uniq | \

uniq連続した重複項目のみを削除

    xargs -d $'\n' -n 1 -I {} env bash -c "

-Iそして-n 1それは重複します。また、bashインラインスクリプトは、各パラメータに対して呼び出すことなく複数のパラメータを使用できますbash

        $(
        cat <<EOF

引用されていないように、EOF拡張はここで次のように実装されています。シェルコード議論。これは悪い習慣であり、典型的なコード注入の脆弱性です。外部データはパラメータまたは環境変数に渡す必要があります。

            filename='{}'

xargs同様に、(by)の拡張は{}シェルコードで終わります。

            filename_length="\${#filename}"
            terminal_width="$(tput cols)"
            delta_length="\$(("\$terminal_width" - "\$filename_length"))"

            # Print filename, and fill width with highlighted background color.
            padding="\$(seq -s' ' "\$(("\$delta_length" + 1))" | \
                tr -d '[:digit:]')"
            printf '\e[1;103;30m%s\e[0m' "\$filename\$padding"

かなり複雑です。私はzsh以下を使用します。

psvar=${(mr[COLUMNS])filename} print -P '%K{yellow}%F{black}%1v%k%f'

他のシェルの場合は、次のように文字列を右埋めることもできます。

printf '%s\t' "$string" | expand -t "$(tput cols)"

少なくともBSDおよびzshのrライトパディング拡張フラグと同様に、mフラグと一緒に使用すると各文字の表示幅が考慮されます(ただしエスケープシーケンスはサポートされていないため、送信されるテキストにカラーシーケンスを含めないでくださいexpand)。

            GROFF_SGR=1 man --no-hyphenation --no-justification \
                "\$filename" | grep --color=always "$arg_search"

[...]

私はあなたがMAN_KEEP_FORMATTINGそれを定義したと仮定しますか?これがないと、パイピング時にフォーマットが失われますgrep

また、man -K固定文字列検索と拡張正規表現検索との--regex両方の使用では大文字と小文字が区別されないため、検索ロジックを一致させるにはオプションと追加-iと追加が--regex必要です。man-Egrep-Fgrep

したがって、ここでは次のようにすることをお勧めします。

#! /bin/zsh -
ere=${1-GROFF_NO_SGR}
typeset -A seen=()
while IFS= read -ru3 filename; do
  if (( ! seen[\$filename]++ )); then
    psvar=${(mr[COLUMNS])filename} print -P '%K{yellow}%F{black}%1v%k%f'
    GROFF_SGR=1 MAN_KEEP_FORMATTING=1 man --no-hyphenation \
      --no-justification -l "$filename" |
      grep -iF --color=always -C4 -- "$ere"
  fi
done 3< <(man -awK -- "$ere")

答え2

私が見つけた唯一の方法は、両方を使用することです。MacOS 12.6 そして Ubuntu 20.04.4 LTS非常に簡単には次のようになります。

MAN_KEEP_FORMATTING=1 man git | ul

(Stéphane Chazelasの答えははるかに詳細であり、多くの時間と専門知識を提供していただきありがとうございます。)

関連情報