AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 496341
Accepted
studog
studog
Asked: 2019-01-24 14:28:01 +0800 CST2019-01-24 14:28:01 +0800 CST 2019-01-24 14:28:01 +0800 CST

为什么 POSIX 需要某些内置的 shell 才能有外部实现?

  • 772

从这个关于 printf 是否是 yash 的内置问题的问题中,得到了这个引用 POSIX 标准的答案。

答案指出,POSIX 搜索序列是找到所需命令的外部实现,然后,如果 shell 已将其实现为内置,则运行内置。(对于不是特殊内置插件的内置插件。)

为什么 POSIX 要求在允许运行内部实现之前存在外部实现?

看起来……随意,所以我很好奇。

shell posix
  • 4 4 个回答
  • 1974 Views

4 个回答

  • Voted
  1. Best Answer
    JdeBP
    2019-01-24T20:51:04+08:002019-01-24T20:51:04+08:00

    这是一个“好像”的规则。

    简而言之:如果实现决定使标准外部命令也可作为内置 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/binfromPATH会调用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 程序调用该函数时看到的常规外部命令相同的行为运行(显然)其他程序无法找到的普通外部命令。从用户的角度来看,一切都是自洽的,并且是控制其工作方式的工具。PATHexecvpe()PATHPATH

    进一步阅读

    • 为什么 POSIX 强制实用程序没有内置到 shell 中?
    • 为什么 echo 是内置命令的 shell?
    • bash如何执行命令
    • 16
  2. mosvy
    2019-01-24T15:44:57+08:002019-01-24T15:44:57+08:00

    这很荒谬,这就是为什么没有 shell 以默认模式实现它的原因。

    该标准的基本原理及其说明性示例表明,这是一个拙劣的尝试,将常规内置与路径相关联,并让用户通过在其前面出现自己的二进制文件来覆盖它PATH(例如printf,与/usr/bin/printf可以/foo/bin/printf通过设置被外部命令覆盖PATH=/foo/bin:$PATH)。

    然而,标准并没有最终要求这样做,而是完全不同的东西(也是无用和意想不到的)。

    您可以在此错误报告中了解更多信息。引自最终接受的文本:

    许多现有实现在不执行 PATH 搜索的情况下执行常规内置。此行为与规范文本不匹配,并且不允许脚本作者通过特制的 PATH 覆盖常规内置实用程序。此外,基本原理解释说,其目的是允许作者 通过修改 PATH 来覆盖内置函数,但这不是规范文本所说的。

    FWIW,我认为也没有任何外壳可以实现接受文本中的修订要求。

    • 15
  3. A. Hendry
    2021-05-26T15:21:50+08:002021-05-26T15:21:50+08:00

    跟进 vis-a-vis echovs printf:

    (下面的 builtin 意思是“特殊内置”,而“常规内置”不被我认为是内置的,因为它们没有内置在外壳中)

    第一个 POSIX 标准化委员会无法就如何标准化回声达成一致,因此他们通过发布如果它被传递标志(-e,-n,-E等)或如果任何参数包含转义序列(\n,\c,\t等),则行为将由定义实现外壳而不是 POSIX。相反,printf添加了该命令并赋予了明确定义的行为。(来源:Robbins 和 Beebe 的经典 Shell 脚本)。

    尽管printf定义明确,但某些 shell 没有printf内置命令(例如mksh)。相反,他们使用printffrom /usr/bin/。这意味着从该 shell 运行的所有脚本都将在给定的操作系统(Ubuntu、Fedora 等)上打印相同的内容,但它们不一定会在操作系统之间打印相同的内容(事实上,许多用户 为此更改printf了/usr/bin原因)。

    或者,printf无论操作系统如何,带有内置命令的 shell 都会打印相同的内容,但前提是使用为 shell 实现的。但是,由于printf行为是由 POSIX 标准定义的,因此程序员不必担心。但是,如果PATH使用printffrom的 shell 被覆盖/usr/bin/,则printf不会被找到。

    尽管所有 shell 都echo具有内置功能,但有些直接解释转义序列(例如ash),而另一些(大多数)需要一个-e标志:行为不是由 POSIX 定义的,而是由 shell 定义的。

    echovs.的主要烦恼之一printf是echo默认情况下会在字符串末尾打印新行,但printf不会。printf 需要\n转义序列来打印新行。相反,为了防止 echo打印新行,\c需要转义序列(可能还需要-e标志)。

    printf推荐用于最大的可移植性,因为它的行为是由 POSIX 定义的,但我个人发现在每行末尾显式打印一个新行非常烦人(我写的大多数行都需要一个新行,我很少需要抑制echo的打印新行)。另一方面, echo始终可用,因为它是内置的(没有在 $PATH 上找不到的风险),并且可以执行简单的检查以确定是否-e需要该标志并生成相应的别名echo:

    #! /bin/sh -
    
    # Determine if "builtin" command exists.
    BUILTIN='builtin'
      
    if ! ("${BUILTIN}" echo 123 >/dev/null 2>&1); then
      BUILTIN=''
    fi
    export BUILTIN
    
    ECHO='echo -e'
    
    if ${BUILTIN} [ "`echo -e test`" = '-e test' ]; then
      ECHO='echo'
    fi
    export ECHO
    
    # Now use "${ECHO}" where you would normally use "echo"...
    

    就个人而言,我更喜欢这样做,并且仅printf在需要特殊格式时才使用。

    更新: 我应该在信用到期时给予适当的信用。上面的 shell 代码直接取自shunit2. 这要归功于 Kate Ward 和shunit2开发团队!(做得好 ;) )

    • 3
  4. A. Hendry
    2021-05-27T20:47:14+08:002021-05-27T20:47:14+08:00

    也添加这个(Robbins 和 Beebe的Classic Shell Scripting是一本很棒的书):

    shell 有许多内置的命令。这意味着 shell 本身执行命令,而不是在单独的进程中运行外部程序。此外,POSIX 区分“特殊”内置函数和“常规”内置函数。[大多数常规内置插件] 必须内置才能使 shell 正常运行(例如,read)。其他的通常只是为了提高效率而内置到外壳中(例如,true和false)。该标准还允许内置其他命令以提高效率,但所有常规内置命令必须可以作为单独的程序访问,这些程序可以由其他二进制程序直接执行。当 shell 搜索要执行的命令时,特殊和常规内置命令之间的区别就会发挥作用。命令搜索的顺序是首先是特殊内置,然后是 shell 函数,然后是常规内置,最后是通过搜索列出的目录找到的外部命令 $PATH。这种搜索顺序使得定义扩展或覆盖常规 shell 内置函数的 shell 函数成为可能。此功能最常用于交互式 shell。例如,假设您希望 shell 的提示符包含当前目录路径名的最后一个组成部分。实现这一点的最简单方法是PS1每次更改目录时都更改 shell。您可以编写自己的 [ cd] 函数 [为此]。这里有一个美中不足的小苍蝇。shell 函数如何访问“真实”cd命令的功能?...需要的是一个“逃生舱”,它告诉 shell 绕过对函数的搜索并访问真实命令。这是command内置命令的工作。

    [然而] 该command命令不是特殊的内置命令!定义了一个名为 command 的函数的 shell 程序员可悲了!POSIX 标准为特殊的内置命令提供了以下两个额外的特性:

    1. 特殊内置实用程序中的语法错误可能会导致执行该实用程序的 shell 中止,而常规内置实用程序中的语法错误不会导致执行该实用程序的 shell 中止。如果遇到语法错误的特殊内置实用程序没有中止 shell,则其退出值应为非零。
    2. 使用特殊内置实用程序指定的变量分配在内置完成后仍然有效;对于常规的内置或其他实用程序,情况不应如此。[即]您可以在命令的前面指定变量赋值,并且该变量将仅在执行命令的环境中具有该值,而不会影响当前shell或后续命令中的变量。(例如PATH=/bin:/usr/bin: awk '...')但是,当这样的赋值与特殊的内置命令一起使用时,赋值从那时起仍然有效,即使在特殊的内置命令完成后也是如此。

    阿诺德罗宾斯和纳尔逊 HF 比比。经典 Shell 脚本:释放 Unix 力量的隐藏命令(第 262-5 页)。奥莱利媒体。Kindle版。

    请注意,该command命令导致 shell 将指定的命令和参数视为简单命令,从而抑制 shell 函数查找。来自IBM 文档

    通常,当 /(斜杠)不在命令之前(指示特定路径)时,shell 通过搜索以下类别来定位命令:

    特殊shell 内置shell 函数 常规shell 内置PATH 环境变量 例如,如果存在与常规内置同名的函数,则系统使用该函数。command 命令允许你调用与函数同名的命令并获取简单的命令。

    命令 -v 和命令 -V 命令将 shell 将使用的路径名以及 shell 如何解释命令类型(内置、函数、别名等)写入标准输出。由于 -v 和 -V 标志产生与当前 shell 环境相关的输出,因此命令命令作为 Korn shell 或 POSIX shell 常规内置命令提供。/usr/bin/command 命令可能不会产生正确的结果,因为它是在子shell 或单独的命令执行环境中调用的。在以下示例中,shell 无法识别别名、子例程或特殊的 shell 命令:

    (PATH=foo 命令 -v) nohup 命令 -v

    因此,在我之前的示例中,我使用了 bash builtin,而不是command因为如果我将它放在子 shell 中,它将无法正常工作。

    我第二个@mosvy:似乎标准和规范文本不匹配(确实很荒谬)。

    • 1

相关问题

  • 这个命令是如何工作的?mkfifo /tmp/f; 猫/tmp/f | /bin/sh -i 2>&1 | 数控 -l 1234 > /tmp/f

  • FreeBSD 的 sh:列出函数

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • grep -v grep 有什么作用

  • 如何将带有〜的路径保存到变量中?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve