의도적으로 형식을 유지하면서 매뉴얼 페이지를 페이징 없이 파이프하려면 어떻게 해야 합니까?

의도적으로 형식을 유지하면서 매뉴얼 페이지를 페이징 없이 파이프하려면 어떻게 해야 합니까?

최소한의 예:

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

에 의해 시작되지 는 않지만 man.less-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

모든 매뉴얼 페이지(전달됨)에서 대소문자를 구분하지 않음(전달됨)을 찾으십시오 precision.i-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-E필요 grep합니다 .-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

내가 찾은 유일한 방법은 두 가지를 모두 사용하는 것입니다.맥OS 12.6 그리고 우분투 20.04.4 LTS매우 간단하게 다음과 같습니다.

MAN_KEEP_FORMATTING=1 man git | ul

(Stéphane Chazelas의 답변은 훨씬 더 자세하고 많은 시간과 전문 지식을 제공해 주셔서 감사합니다.)

관련 정보