使用包含 NULL 字符的正则表达式字符范围进行 Grep

Grep with a regex character range that includes the NULL character

当我在 BSD grep 的正则表达式字符范围内包含 NULL 字符 (\x00) 时,结果出乎意料:没有字符匹配。为什么会这样?

这是一个例子:

$ echo 'ABCabc<>/ă' | grep -o [$'\x00'-$'\x7f']

这里我希望所有字符直到最后一个匹配,但是结果是没有输出(没有匹配)。

或者,当我从 \x01 开始字符范围时,它按预期工作:

$ echo 'ABCabc<>/ă' | grep -o [$'\x01'-$'\x7f']
A
B
C
a
b
c
<
>
/

此外,这是我的 grep 和 BASH 版本:

$ grep --version
grep (BSD grep) 2.5.1-FreeBSD

$ echo $BASH_VERSION
3.2.57(1)-release

注意到$'...' is a shell quoting construct,这个,

$ echo 'ABCabc<>/ă' | grep -o [$'\x00'-$'\x7f']

会尝试将文字 NUL 字符作为命令行参数的一部分传递给 grep。这在任何类 Unix 系统中都是不可能的,因为命令行参数作为以 NUL 结尾的字符串传递给进程。所以实际上,grep 只看到参数 -o[.

您需要创建一些匹配 NUL 字节的模式,而不是按字面意思包含它。但我认为 grep 不支持 [=18=]0\x00 逃逸本身。不过,Perl 确实如此,因此它会打印带有 NUL:

的输入行
$ printf 'foo\nbar[=11=]\n' |perl -ne 'print if /[=11=]0/'
bar

顺便说一句,至少 GNU grep 似乎不喜欢那种范围表达式,所以如果你要使用它,你需要做一些不同的事情。在 C 语言环境中,[[:cntrl:][:print:]]' 可能会匹配从 \x01\x7f 的字符,但我没有全面检查。 manual for grep has some descriptions of the classes.


另请注意,[$'\x00'-$'\x7f'] 有一对未加引号的 [],shell 也是如此。这与 NUL 字节无关,但是如果你有匹配 glob 的文件(任何一个字母的名称,如果 glob 在你的系统上工作——它不在我的 Linux 上),或者有failglobnullglob 集,它可能会产生您不想要的结果。相反,也引用括号:$'[\x00-\x7f]'.

在 BSD grep 上,您可以使用这个:

LC_ALL=C grep -o '[[:print:][:cntrl:]]' <<< 'ABCabc<>/ă'

A
B
C
a
b
c
<
>
/

或者您可以使用 home brew 软件包和 运行:

安装 gnu grep
grep -oP '[[:ascii:]]' <<< 'ABCabc<>/ă'