从这个关于 printf 是否是 yash 的内置问题的问题中,得到了这个引用 POSIX 标准的答案。
答案指出,POSIX 搜索序列是找到所需命令的外部实现,然后,如果 shell 已将其实现为内置,则运行内置。(对于不是特殊内置插件的内置插件。)
为什么 POSIX 要求在允许运行内部实现之前存在外部实现?
看起来……随意,所以我很好奇。
从这个关于 printf 是否是 yash 的内置问题的问题中,得到了这个引用 POSIX 标准的答案。
答案指出,POSIX 搜索序列是找到所需命令的外部实现,然后,如果 shell 已将其实现为内置,则运行内置。(对于不是特殊内置插件的内置插件。)
为什么 POSIX 要求在允许运行内部实现之前存在外部实现?
看起来……随意,所以我很好奇。
这是一个“好像”的规则。
简而言之:如果实现决定使标准外部命令也可作为内置 shell 使用,那么用户所看到的 shell 的行为不应该改变。
我在https://unix.stackexchange.com/a/496291/5132展示的(一方面)PD Korn、MirBSD Korn 和 Heirloom Bourne shell 的行为之间的对比;(另一方面)Z、93 Korn、Bourne Again 和 Debian Almquist shell;并且(在紧握的手上)Watanabe 贝壳突出了这一点。
对于没有
printf
内置的 shell,删除/usr/bin
fromPATH
会调用printf
停止工作。Watanabe shell 在其一致性模式下表现出的 POSIX 一致性行为会导致相同的结果。具有printf
内置功能的 shell 的行为就像调用外部命令一样。/usr/bin
而如果从 中删除,所有不符合要求的 shell 的行为都不会改变PATH
,并且它们的行为不像调用外部命令一样。该标准试图向您保证的是,shell 可以内置各种通常的外部命令(或将它们实现为自己的 shell 函数),并且您仍然会从内置命令中获得与您所做的相同的行为如果您调整
PATH
以停止找到命令,则使用外部命令。PATH
仍然是您选择和控制可以调用的命令的工具。(正如https://unix.stackexchange.com/a/448799/5132中所解释的,几年前人们通过改变 on 来选择他们的 Unix 的个性
PATH
。)有人可能会认为,无论是否可以找到命令,使命令始终有效
PATH
实际上是使通常的外部命令内置的关键。(这就是为什么我的 nosh 工具集在 1.38 版中刚刚获得了一个内置printenv
命令,事实上。虽然这不是一个 shell。)但是该标准向您保证,您将看到与从其他非 shell 程序调用该函数时看到的常规外部命令相同的行为运行(显然)其他程序无法找到的普通外部命令。从用户的角度来看,一切都是自洽的,并且是控制其工作方式的工具。
PATH
execvpe()
PATH
PATH
进一步阅读
这很荒谬,这就是为什么没有 shell 以默认模式实现它的原因。
该标准的基本原理及其说明性示例表明,这是一个拙劣的尝试,将常规内置与路径相关联,并让用户通过在其前面出现自己的二进制文件来覆盖它
PATH
(例如printf
,与/usr/bin/printf
可以/foo/bin/printf
通过设置被外部命令覆盖PATH=/foo/bin:$PATH
)。然而,标准并没有最终要求这样做,而是完全不同的东西(也是无用和意想不到的)。
您可以在此错误报告中了解更多信息。引自最终接受的文本:
FWIW,我认为也没有任何外壳可以实现接受文本中的修订要求。
跟进 vis-a-vis
echo
vsprintf
:(下面的
builtin
意思是“特殊内置”,而“常规内置”不被我认为是内置的,因为它们没有内置在外壳中)第一个 POSIX 标准化委员会无法就如何标准化回声达成一致,因此他们通过发布如果它被传递标志(
-e,-n,-E
等)或如果任何参数包含转义序列(\n,\c,\t
等),则行为将由定义实现外壳而不是 POSIX。相反,printf
添加了该命令并赋予了明确定义的行为。(来源:Robbins 和 Beebe 的经典 Shell 脚本)。尽管
printf
定义明确,但某些 shell 没有printf
内置命令(例如mksh
)。相反,他们使用printf
from/usr/bin/
。这意味着从该 shell 运行的所有脚本都将在给定的操作系统(Ubuntu、Fedora 等)上打印相同的内容,但它们不一定会在操作系统之间打印相同的内容(事实上,许多用户 为此更改printf
了/usr/bin
原因)。或者,
printf
无论操作系统如何,带有内置命令的 shell 都会打印相同的内容,但前提是使用为 shell 实现的。但是,由于printf
行为是由 POSIX 标准定义的,因此程序员不必担心。但是,如果PATH
使用printf
from的 shell 被覆盖/usr/bin/
,则printf
不会被找到。尽管所有 shell 都
echo
具有内置功能,但有些直接解释转义序列(例如ash
),而另一些(大多数)需要一个-e
标志:行为不是由 POSIX 定义的,而是由 shell 定义的。echo
vs.的主要烦恼之一printf
是echo
默认情况下会在字符串末尾打印新行,但printf
不会。printf
需要\n
转义序列来打印新行。相反,为了防止echo
打印新行,\c
需要转义序列(可能还需要-e
标志)。printf
推荐用于最大的可移植性,因为它的行为是由 POSIX 定义的,但我个人发现在每行末尾显式打印一个新行非常烦人(我写的大多数行都需要一个新行,我很少需要抑制echo
的打印新行)。另一方面,echo
始终可用,因为它是内置的(没有在 $PATH 上找不到的风险),并且可以执行简单的检查以确定是否-e
需要该标志并生成相应的别名echo
:就个人而言,我更喜欢这样做,并且仅
printf
在需要特殊格式时才使用。更新: 我应该在信用到期时给予适当的信用。上面的 shell 代码直接取自
shunit2
. 这要归功于 Kate Ward 和shunit2
开发团队!(做得好 ;) )也添加这个(Robbins 和 Beebe的Classic Shell Scripting是一本很棒的书):
阿诺德罗宾斯和纳尔逊 HF 比比。经典 Shell 脚本:释放 Unix 力量的隐藏命令(第 262-5 页)。奥莱利媒体。Kindle版。
请注意,该
command
命令导致 shell 将指定的命令和参数视为简单命令,从而抑制 shell 函数查找。来自IBM 文档因此,在我之前的示例中,我使用了 bash
builtin
,而不是command
因为如果我将它放在子 shell 中,它将无法正常工作。我第二个@mosvy:似乎标准和规范文本不匹配(确实很荒谬)。