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 -x在man-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,不会传递给grotty并grotty恢复为在那里使用 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,但是如果查找只有部分为粗体/下划线的文本,您仍然可能会遇到问题...
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。