Bash

如何在有意保留格式的同時通過管道傳輸手冊頁(沒有分頁)?

  • September 10, 2022

最小的例子:

man git | cat

真實例子:

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

FWIW,我嘗試過--pager="cat"使用各種尋呼機,以及各種尋呼機的管道。我也嘗試過使用vimcat相同的,但不幸的是它會凍結。我什至嘗試過使用unbuffered. 一切都無濟於事。

也許可以使用-Tor {g,n,t}roff,但我不確定如何?有沒有辦法我可以通過管道傳輸到less/ vim/etc 並讓它通過格式化(顏色/等)傳遞到標準輸出而無需分頁




**編輯:**感謝下面 Stéphane 的幫助,我現在有了一種輕鬆搜尋(並突出顯示!)所有匹配的手冊頁的漂亮方法。這對我來說是一個改變遊戲規則的人。謝謝你,斯蒂芬!

在此處輸入圖像描述

對於那些可能感興趣的人,這裡是腳本的關鍵部分(毫無疑問它可以改進;隨時歡迎回饋):

arg_search="GROFF_NO_SGR"

# The reason for this complicated pipeline is that searching man pages can take
# a very long time, so line buffer the output (using stdbuf) and immediately
# process it, rather than waiting for the entire search to complete.
man -w -a -K "$arg_search" | \
   stdbuf -oL -eL uniq | \
   xargs -d $'\n' -n 1 -I {} env bash -c "
       $(
       cat <<EOF
           filename='{}'
           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"

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

嘗試:

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

如果管道到grep,您可能希望GROFF_SGR=1(至少在基於 Debian 的系統上需要)使用 ANSI SGR 轉義序列(如 )實現粗體/下劃線,\e[1mBOLD\e[m而不是傳統的B\bBO\bOL\bLD\bD難以 grep 的方法。有關詳細資訊,請參閱Grep:在手冊頁標題中搜尋單詞時出現意外結果

請注意,尋呼機(此處grep)僅在man的標準輸出進入終端時執行。管道到諸如less將禁用該命令grep和格式的命令。

MAN_KEEP_FORMATTING當我回答 Grep 時,在我的測試中用於禁用 SGR 的設置:當時從手冊頁的標題中搜尋單詞時出現意外結果,但似乎不再是這種情況(至少在 Ubuntu 20.04 上不是)。

因此,要使用尋呼機,您可以這樣做

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

雖然它不是由 啟動的manless但未配置為顯示相關的頁腳行,或者您可以將內聯 shell 腳本傳遞給-P該呼叫greppager.

例如,使用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

如果您的pageris less,因為它是由 開始的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"}
}

(使用"$*"andIFS=' '而不是j[ ]加入空格,@Q(需要bash解碼)而不是(qq)(執行 sh 兼容的引用),使用shift並保存第一個參數$pageas@Q不能與數組範圍結合)。

允許man(in 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

在ll手冊頁(傳遞給)中查找precision大小寫i敏感(-i傳遞給)。grep``a``printf``-a``man

使其成為腳本而不是函式:

#! /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]"

然後您將能夠從任何 shell(或非 shell)中使用,而不僅僅是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未引用的那樣,在此處的文件中正在執行擴展,並將其作為shell 程式碼參數傳遞。這是不好的做法,這是典型的程式碼注入漏洞。外部數據應作為參數或環境變數傳遞。

            filename='{}'

同樣,擴展 (by xargs){}最終出現在 shell 程式碼中。

            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'

使用其他 shell,您還可以使用以下方法對字元串進行右填充:

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

至少在 BSD 和 zsh 的right 填充擴展標誌與m標誌一起使用時會考慮每個字元的顯示寬度(但不支持轉義序列,因此著色的不能包含在發送到的文本中expand)。

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

$$ … $$ 我想你已經MAN_KEEP_FORMATTING定義了?沒有它,當管道傳輸到grep.

另請注意,man -K使用 and 進行固定字元串搜尋和擴展正則表達式搜尋--regex都不區分大小寫,因此您需要該-i選項並添加--regexman-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")

引用自:https://unix.stackexchange.com/questions/716765