如何在grep中使用POSIXLY_CORRECT?

11

Bash中有一个变量POSIXLY_CORRECT

POSIXLY_CORRECT

If this variable is in the environment when Bash starts, the shell enters POSIX mode (see Bash POSIX Mode) before reading the startup files, as if the --posix invocation option had been supplied. If it is set while the shell is running, Bash enables POSIX mode, as if the command

set -o posix

had been executed.

我被告知一些grep的选项不是POSIX标准,所以我在 The Open Group Base Specifications Issue 6 for grep中进行了确认。因此,我查看了 GNU grep手册并发现:

grep带有丰富的选项:其中一些来自POSIX,另一些则是GNU扩展。长选项名称始终是GNU扩展,即使是来自POSIX规范的选项也是如此。由POSIX指定的选项,在其短名称下明确标记为这样,以便促进POSIX可移植编程。一些选项名称是为与旧版或更奇特的实现兼容而提供的。

它还提到:

2.2 环境变量

grep 的行为受以下环境变量的影响。

POSIXLY_CORRECT
如果设置了此变量,grep 将按照 POSIX 要求运行;否则,grep 将更像其他 GNU 程序。POSIX 要求后面跟随文件名的选项必须被视为文件名;默认情况下,这些选项会被置于操作数列表的前面并被视为选项。此外,POSIXLY_CORRECT 禁用对无效括号表达式的特殊处理。请参见 invalid-bracket-expr。

使用部分 长选项名称始终是 GNU 扩展,即使是来自 POSIX 规范的选项也是如此,我说:让我们尝试使用变量 POSIXLY_CORRECT 对此进行测试。

因此,我尝试了一些不符合 POSIX 的内容:

$ echo "HELLO" | grep --ignore-case 'hello'
HELLO

但令我惊讶的是,它也可以通过设置来工作:
$ echo "HELLO" | POSIXLY_CORRECT=1 grep --ignore-case 'hello'
HELLO

我做错了什么?使用 POSIXLY_CORRECT 设置不应该使 grep 无法识别长选项名称吗?

如果使用非 POSIX 的选项(例如 -C),情况也是一样的:

$ POSIXLY_CORRECT=1 grep -C 2 '2' <<< "1
2
3"
1
2
3

在运行之前,同样需要执行set -o posix


1
grep 是一个外部程序,它并不属于 shell 的一部分。 - Barmar
2
@cdarke 当您将变量赋值放在命令开头时,它会自动导出。 - Barmar
我投票关闭此问题,因为这个问题更适合在unix.stackexchange.com上提问。这是关于特定Unix程序设计的问题,而不是编程问题。 - Barmar
3
@Barmar,嗯,我不同意。这是关于程序员常用的“软件工具”的问题,详见What topics can I ask about here? - fedorqui
1
grep 是所有 Unix 用户都使用的软件工具,不仅限于程序员。程序员也使用 ls 命令来列出包含源文件的目录,但这并不使其成为这里讨论的话题。 - Barmar
显示剩余3条评论
2个回答

9

从GNU grep 手册中:

POSIXLY_CORRECT

如果设置了,grep的行为就像POSIX要求的那样;否则,grep的行为更像其他GNU程序。 POSIX要求后跟文件名的选项必须被视为文件名; 默认情况下,这些选项会被置于操作数列表的前面并被视为选项。此外,POSIX要求未识别的选项应被诊断为“非法”,但由于它们实际上不违反法律,因此默认情况下是将它们诊断为“无效”。 POSIXLY_CORRECT还禁用了下文描述的_N_GNU_nonoption_argv_flags_

这意味着在环境中设置POSIXLY_CORRECT只有一个作用,即GNU grep不允许重新排列文件名后出现的选项,使它们位于前面。它不会使grep不接受非POSIX命令行标志。

所以来试试吧:

$ ggrep "hello" myfile -v

$ env POSIXLY_CORRECT=1 ggrep "hello" myfile -v
ggrep: -v: No such file or directory

(在我的BSD系统上,GNU grep 被称为 ggrep)

关于手册中的“未识别选项”部分是GNU grep的默认行为,即-g标志将在使用和不使用POSIXLY_CORRECT时被诊断为“无效”。例如,--ignore-case 是一个有效选项(虽然不是POSIX),因此在使用POSIXLY_CORRECT时不会被诊断为“无效”。

一般来说,请查看外部实用程序的文档,了解它们在POSIXLY_CORRECT下的行为方式(如果它们真的关心的话)。bash手册只能告诉您如何受此环境变量影响的shell及其内置命令。


3
你以微弱的优势击败了我。也许需要强调Bash文档仅适用于Bash,而“POSIXLY_CORRECT”设置确切地执行了grep手册所述的操作。 - tripleee
1
“grep规范”(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html)通过引用“实用程序语法指南”(http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02)来整合,后者指出:“每个选项名称应该是一个单个的字母数字字符[...]”,因此这可能是违反POSIX的。然而,GNU从一开始就没有严格遵守POSIX。POSIXLY_CORRECT始终是一个可移植设备,并且从未旨在提供完全的POSIX兼容性。 - Kevin

3
首先,一般来说,POSIXLY_CORRECT 变量是由一些 GNU 工具和库函数使用的,它试图更加符合 POSIX 标准,但并不保证 GNU 工具严格遵循 POSIX 标准。
在选项解析方面,GNU grep 并没有读取 POSIXLY_CORRECT 变量。GNU grep 使用 glibc 函数 getopt_long 来解析选项。该函数仅以有限的方式尊重 POSIXLY_CORRECT 环境变量。请查看 man getopt_long

POSIXLY_CORRECT

如果设置了此变量,则当遇到非选项参数时,选项处理会立即停止。

...以及 GNU grep 的源代码

对于所有链接到 glibc 并使用 getopt_long 的程序,其行为都是相同的。这与 grep 无关。

所以这个变量是我的误解。我以为 POSIXLY_CORRECT 会让我回到 POSIX grep 版本的历史中,但实际上并不是这样的。 - fedorqui
不是这样的。我想这总是一个代码复杂性、性能、向后兼容性以及是,符合 posix 的权衡。 - hek2mgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接