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


19

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

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

如果我为grep name获得了结果,但是如果我为grep获得NAME,则不会:

$ 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
}


您使用什么操作系统?我确定接受的答案是正确的,但IO无法在我的Arch Linux机器上重现该答案。man bash | grep NAME可以正常工作。
terdon

@terdon我在macOS上。我在Bash 3.2和4.4.5中遇到了这种情况
ivan

顺便说一句:如果您检测到内置函数,则可以使用bash help命令获取其信息。

@Joe问题是我经常发现help结果遗漏了太多。例如,查看help completevs中的complete部分man bash
伊凡(Ivan)

Answers:


33

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

N\bNA\bAM\bME\bE

也就是说,每个字符都写为XBackspace X。在现代终端上,字符最终被自己覆盖(因为Backspace aka BS aka \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 "$@"

您在中放在/ 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环境变量(或不GROFF_NO_SGR根据在编译时如何设置默认值来设置变量),则grotty(只要未通过该-c选项就设置)将使用ANSI SGR终端转义序列这些BS角色属性的技巧。less使用-R选项调用时了解它们。

FreeBSD的人呼吁grotty-c,除非你要求选择颜色通过设置MANCOLOR变量(在这种情况下,-c不传递到grottygrotty还原为使用ANSI SGR转义序列存在的默认设置)。

MANCOLOR=1 man bash | grep NAME

将在那里工作。

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

GROFF_SGR=1 man bash | grep NAME

但是,由于manstdout不是终端,因此它本身也需要将一个GROFF_NO_SGR变量传递给grotty(我想这样,它可以col -bpx用来剥离BS序列,因为col它不知道如何剥离SGR序列,即使它仍然用MAN_KEEP_FORMATTING)覆盖我们的GROFF_SGR。您可以改为:

GROFF_SGR=1 MANPAGER='grep NAME' man bash

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

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

\033[1mNAME\033[0m$

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

在该文本上grep NAME可以像该文本所包含的那样工作NAME,但是如果只查找其中一部分为粗体/下划线的文本,您仍然可能会遇到问题...


2
哇,很高兴看到那里的物理远程打字机。两倍墨水=>粗体。完全有道理
伊凡(Ivan)

1
我很喜欢sed -n l代替od
汤姆·黑尔

13

如果查看任何手册页,您会发现标题为粗体。这是通过用控制字符格式化它们来实现的。为了能够grep像您想要的那样,这些必须被去除。

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

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

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

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


Linux的 col手册(在Ubuntu上)没有最后一句话(但以相同的方式起作用)。

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


我认为(就像我在Arch和Ubuntu系统上测试的那样)在Linux上这是没有必要的,或者不再是。在两个系统上,NAMEbash手册中的just NAME,no \b
terdon

@terdon我并没有首先提到macOS,所以我认为可能是配置错误的Linux系统。我现在已经修剪掉Linux的位。
库桑兰达

您什么都没错过,我问OP他们正在使用什么操作系统,因为我无法在Linux上进行复制,他们说macOS,我现在就添加了它。我并不是在暗示您错了,因为我所知道的所有Linux发行版中的MAN_KEEP_FORMATTING变量都完全按照您所说的那样工作。我只是想指出并非总是如此。
terdon
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.