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)
也就是说,每个字符都写为X
Backspace X
。在现代终端上,字符最终会被覆盖(因为 Backspace aka BS \b
aka^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
无需传递man
through的输出col -b
。