如何在有意保留格式的同时对手册页进行管道传输(不分页)?

如何在有意保留格式的同时对手册页进行管道传输(不分页)?

最小的例子:

man git | cat

真实例子:

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

FWIW,我尝试过--pager="cat"使用各种寻呼机,以及通过管道连接到各种寻呼机。我也尝试过使用vimcat相同的,但不幸的是它冻结了。我什至尝试过使用unbuffered.一切都无济于事。

也许可以使用-Tor {g,n,t}roff,但我不确定如何?有没有办法可以通过管道传输到less/ vim/etc 并将其传递到 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

如果通过管道传输到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当我回答时,用于在我的测试中禁用 SGR 的设置Grep:从手册页搜索标题中的单词时出现意外结果那时,但现在似乎不再是这样了(至少在 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

如果您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"}
}

(使用"$*"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

在所有手册页(传递给)中不区分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]"

然后您将能够从任何 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未引用的那样,扩展正在此处文档中执行,并且作为外壳代码争论。这是不好的做法,这是典型的代码注入漏洞。外部数据应该作为参数或环境变量传递。

            filename='{}'

同样的, 的扩展(由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")

答案2

我发现的唯一可以同时使用这两种方法的方法macOS 12.6 Ubuntu 20.04.4 LTS非常简单如下:

MAN_KEEP_FORMATTING=1 man git | ul

(我将 Stéphane Chazelas 的答案保留为已接受的答案,因为它要详细得多,并感谢您提供大量时间和专业知识。)

相关内容