1

当我在 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
4

2 回答 2

3

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

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

A
B
C
a
b
c
<
>
/

或者您可以gnu grep使用home brewpackage 安装并运行:

grep -oP '[[:ascii:]]' <<< 'ABCabc<>/ă'
于 2021-03-01T19:38:56.083 回答
2

注意到这$'...'是一个shell引用结构,this,

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

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

您需要创建一些与 NUL 字节匹配的模式,而不是从字面上包含它。但我不认为grep支持\000\x00逃避本身。不过,Perl 确实如此,所以这会打印带有 NUL 的输入行:

$ printf 'foo\nbar\0\n' |perl -ne 'print if /\000/'
bar

顺便说一句,至少 GNU grep 似乎不喜欢那种范围表达式,所以如果你要使用它,你会做一些不同的事情。在C语言环境中,[[:cntrl:][:print:]]'也许可以匹配从\x01to的字符\x7f,但我没有全面检查。grep的手册有一些类的描述


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

于 2021-03-01T19:22:57.297 回答