问题描述
当我在 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
解决方法
在 BSD grep
上,您或许可以使用:
LC_ALL=C grep -o '[[:print:][:cntrl:]]' <<< 'ABCabc<>/ă'
A
B
C
a
b
c
<
>
/
或者您可以使用 gnu grep
包安装 home brew
并运行:
grep -oP '[[:ascii:]]' <<< 'ABCabc<>/ă'
,
注意到 $'...'
is a shell quoting construct,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:]]'
可能可以匹配从 \x01
到 \x7f
的字符,但我没有进行全面检查。
manual for grep has some descriptions of the classes。
还要注意 [$'\x00'-$'\x7f']
有一对未加引号的 [
和 ]
,因此是一个 shell glob。这与 NUL 字节无关,但如果您有与 glob 匹配的文件(任何一个字母的名称,如果 glob 在您的系统上运行——它在我的 Linux 上不可用),或者有 {{1} } 或 failglob
设置,它可能会给出您不想要的结果。相反,也将括号括起来:nullglob
。