Grep:从手册页搜索标题中的单词时出现意外结果

Grep:从手册页搜索标题中的单词时出现意外结果

当我尝试在 macOS 上 grep 手册页时,遇到了奇怪的行为。例如,Bash 手册页明显出现了以下字符串NAME

$ man bash | head -5 | tail -1
NAME

如果我 grep forname我确实得到了结果,但如果我 grep forNAME我没有:

$ man bash | grep 'NAME'
$ man bash | grep NAME

我已经尝试过我知道的其他大写单词,并且搜索SHELL不会产生任何结果,而搜索会BASH产生结果。

这里发生了什么?

更新: 感谢所有的答案!我认为值得添加我遇到这个问题的背景。我想编写一个 bash 函数来包装man,如果我尝试查找 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
}

答案1

如果您| sed -n l向该tail命令添加 a 以显示不可打印的字符,您可能会看到类似以下内容的内容:

N\bNA\bAM\bME\bE

也就是说,每个字符都写为XBackspace X。在现代终端上,字符最终会被自己覆盖(因为 Backspace 又名 BS 又名\b^H将光标向左移动一列的字符),没有任何区别。但在古代的电传打字机中,这会导致字符以粗体显示,因为它的墨水量是原来的两倍。

尽管如此,像more/这样的寻呼机less确实理解该格式意味着粗体,所以这仍然是roff输出粗体文本的方法。

一些 man 实现会roff以不使用这些序列的方式调用(或者col -b -p -x像实现的情况一样在内部调用以剥离它们man-db(除非MAN_KEEP_FORMATTING设置了环境变量)),并且在检测到输出时不调用寻呼机不会去终端(所以man bash | grep NAME可以在那里工作),但不是你的。

您可以使用col -b来删除这些序列(还有其他类型(_BS X)以及下划线)。

对于使用 GNU 的系统(如 GNU 或 FreeBSD),您可以通过确保选项传递到 来roff避免首先使用这些序列,例如通过确保选项传递到。-c -b -ugrotty-P-cbugroff

例如,通过创建一个名为“groff包含”的包装脚本:

#! /bin/sh -
exec /usr/bin/groff -P-cbu "$@"

您将其放在 /usr/bin/groff 之前$PATH

使用 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 roff,如果您设置环境变量(或者根据编译时默认值的设置方式GROFF_SGR不设置变量),那么(只要没有传递该选项)将使用 ANSI SGR 终端转义序列那些关于角色属性的废话。当使用选项调用时理解它们。GROFF_NO_SGRgrotty-cless-R

FreeBSD 的 man 会grotty调用该-c选项,除非你要求颜色通过设置 MANCOLOR 变量(在这种情况下-c不会传递到grottygrotty恢复为使用 ANSI SGR 转义序列的默认值)。

MANCOLOR=1 man bash | grep NAME

将在那里工作。

在 Debian 上,GROFF_SGR 不是默认值。如果您这样做:

GROFF_SGR=1 man bash | grep NAME

然而,因为man's stdout 不是终端,它本身也将变量传递GROFF_NO_SGRgrotty(我想它可以用来col -bpx剥离 BS 序列,因为col不知道如何剥离 SGR 序列,即使它仍然与MAN_KEEP_FORMATTING)一起执行,它会覆盖我们的GROFF_SGR.你可以这样做:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

(在终端中)获得 SGR 转义序列。

那时,你会注意到其中一些姓名s 在终端(和寻呼机)上以粗体显示less -R。如果将输出输入sed -n l( MANPAGER='sed -n /NAME/l'),您将看到类似以下内容:

\033[1mNAME\033[0m$

其中\e[1m是在 ANSI 兼容终端中启用粗体的序列,以及\e[0m将所有 SGR 属性恢复为默认值的序列。

该文本的grep NAME工作原理是该文本确实包含NAME,但如果查找仅部分内容为粗体/下划线的文本,您仍然可能会遇到问题...

答案2

如果您查看任何手册页,您会发现标题以粗体显示。这是通过使用控制字符格式化它们来实现的。为了能够grep喜欢你想要的,这些都必须被剔除。

col实用程序可用于此目的:

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

-b选项有以下描述在 OpenBSD 上

不输出任何退格键,仅打印写入每列位置的最后一个字符。这对于处理 mandoc(1) 的输出很有用。


Linuxcol手册(在 Ubuntu 上)没有最后一句(但它的工作方式相同)。

在 Linux 上,取消设置MAN_KEEP_FORMATTING环境变量(或将其设置为空字符串)也可能有所帮助,并且允许您无需传递throughgrep的输出。mancol -b

相关内容