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

iva*_*van 20 grep man special-characters

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

$ man bash | head -5 | tail -1
NAME
Run Code Online (Sandbox Code Playgroud)

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

$ man bash | grep 'NAME'
$ man bash | grep NAME
Run Code Online (Sandbox Code Playgroud)

我试过其他我知道的大写单词,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
}
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 33

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

N\bNA\bAM\bME\bE
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

#! /bin/sh -
exec /usr/bin/groff -P-cbu "$@"
Run Code Online (Sandbox Code Playgroud)

你把 /usr/bin/groff 放在$PATH.

使用 macOS' man(也使用 GNU roff),您可以创建一个man-no-overstrike.conf

NROFF /usr/bin/groff -mandoc -Tutf8 -P-cbu
Run Code Online (Sandbox Code Playgroud)

并调用man为:

man -C man-no-overstrike.conf bash | grep NAME
Run Code Online (Sandbox Code Playgroud)

仍然使用 GNU roff,如果您设置GROFF_SGR环境变量(或者不GROFF_NO_SGR根据编译时默认设置的方式设置变量),那么grotty(只要它没有传递-c选项)将使用 ANSI SGR 终端转义序列那些针对角色属性的 BS 技巧。less使用-R选项调用时理解它们。

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

MANCOLOR=1 man bash | grep NAME
Run Code Online (Sandbox Code Playgroud)

会在那里工作。

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

GROFF_SGR=1 man bash | grep NAME
Run Code Online (Sandbox Code Playgroud)

然而,因为man的标准输出不是终端,它自己也将一个GROFF_NO_SGR变量传递给grotty(我想所以它可以col -bpx用来剥离 BS 序列,因为col不知道如何剥离 SGR 序列,即使它仍然用MAN_KEEP_FORMATTING) 来覆盖我们的GROFF_SGR. 你可以这样做:

GROFF_SGR=1 MANPAGER='grep NAME' man bash
Run Code Online (Sandbox Code Playgroud)

(在终端中)具有 SGR 转义序列。

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

\033[1mNAME\033[0m$
Run Code Online (Sandbox Code Playgroud)

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

在该文本上的grep NAME工作方式与该文本确实包含一样NAME,但是如果查找只有部分为粗体/下划线的文本,您仍然可能会遇到问题...

  • 哇,在那里看到物理电传类型的遗产非常有趣。两倍的墨水 => 粗体。很有道理 (2认同)
  • 我喜欢用 `sed -nl` 作为 `od` 的替代品。 (2认同)

Kus*_*nda 13

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

col实用程序可用于:

$ man bash | col -b | grep 'NAME'
Run Code Online (Sandbox Code Playgroud)

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

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


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

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