当“/bin/sh”指向“/b​​in/bash”时运行 shell 脚本

当“/bin/sh”指向“/b​​in/bash”时运行 shell 脚本

我已阅读此内容中的以下内容问题:

bash 支持 --posix 开关,这使得它更符合 POSIX 标准。它还尝试模仿 POSIX 如果调用为 sh

上面的引用假设/bin/sh是一个指向 的链接/bin/bash

但我不太明白是什么意思“作为 sh 调用”


假设我有以下名为“script.sh”的脚本:

#!/bin/bash
echo "Hello World"

请告诉我在以下每种情况下脚本是否会在正常bash模式或 POSIX 模式下运行(假设我在正在运行的终端中执行了以下命令bash):

  1. sh script.sh
  2. bash script.sh
  3. ./script.sh

现在假设我有以下名为“script.sh”的脚本(类似于上面的脚本,但没有 shebang):

echo "Hello World"

请告诉我在以下每种情况下脚本是否会在正常bash模式或 POSIX 模式下运行(假设我在正在运行的终端中执行了以下命令bash):

  1. sh script2.sh
  2. bash script2.sh
  3. ./script2.sh

答案1

只有情况 1 和 4 会在 POSIX 模式下运行(假设是shbash 而不是 sh 的其他实现)。任何明确调用bashwithout 的情况--posix都不会,无论是否来自 shebang。任何明确调用 will 的情况sh。仅当尚未为脚本显式启动 shell 时才使用 shebang。

情况 6,如果您的终端正在运行bash,则不会以 POSIX 模式运行并且Bash 将使用自身调用它。如果您的终端运行的是 zsh,则情况 6也运行在 POSIX 模式下。 POSIX 对于在这种情况下到底应该发生什么是不明确的,Bash 和 zsh 在那里做出了不同的选择。 Bash 使用自身调用脚本,而 zsh 使用sh(无论发生什么情况)。其他 shell 在这一点上也有所不同。


判断您所处模式的一种简单方法是创建脚本主体:

kill -SIGHUP

这将在 POSIX 模式下失败并出现错误kill,但给出其外部的使用说明。这是一个简单的区别,它适用于很长范围的 Bash 版本,可以追溯到您可能遇到的版本。

答案2

“调用为”指的是启动 Bash 的进程放入其“第零个”命令行参数中的任何内容argv[0]

当程序启动时exec*()系统调用,他们并不真正知道包含程序的二进制文件的名称,而是调用进程可以自由地将其想要的任何内容放在那里。当然,通常情况下,该名称是从文件系统中获取的,因此如果您运行/bin/sh,那么该名称就会被放在那里。如果/bin/sh是 Bash,则它不一定是符号链接,它可以是硬链接或只是 shell 程序的另一个副本。

作为设置“程序名称”的示例,Bash 的exec命令可以使用选项设置第 0 个参数-a。 (我们可以用 Perl 做同样的事情,或者直接用 C 等)

这里myname是一个简单的 C 程序,它只打印它的第 0 个参数,即它自己看到的名称:

$ ./myname 
I am ./myname
$ (exec -a something ./myname )
I am something
$ mv ./myname somename
$ ln -s somename othername
$ ./somename 
I am ./somename
$ ./othername
I am ./othername

来源:

#include <stdio.h>
int main(int argc, char *argv[]) {
    printf("I am %s\n", argv[0]);
    return 0;
}

但是,要回答编号问题...

(1 & 4) 运行sh somescript将运行sh你的任何东西PATH,可能/bin/sh但是可能是这样的/usr/xpg4/bin/sh

  • 如果是 Bash,它会以 POSIX 模式运行,因为它会看到名称sh.
  • 如果它是 Z shell 或 Korn shell,它同样会看到名称sh,但它以“SH 兼​​容”模式运行,该模式旨在兼容 Bourne shell,并且与这两个 shell 中的完整 POSIX 一致模式略有不同。
  • 当然,它可能是 Almquist shell、实际的 Bourne shell 或其他东西。

(2 & 5) 运行bash somescript将以常规 Bash 模式运行(同样,这当然取决于bash您的PATH内容。)

(3) 这里,脚本的名称直接提供给系统调用,而不是程序文件。内核读取 hashbang 行并使用它来运行脚本。

(6) 这是一个复杂的问题。它与(3)类似,但是启动程序的系统调用失败(ENOEXEC (Exec format error)),因为没有 hashbang 行。接下来会发生什么取决于您正在运行的 shell 是否是本身在 POSIX 模式下。POSIX 要求符合 POSIX 的 shell 以特定方式运行以响应ENOEXEC. 但是,“相当于调用 shell 的命令”存在一些余地,这意味着不同的 shell 会执行不同的操作。

  • 谍影重重 shell以相同模式重新运行将脚本的名称作为其第一个命令行参数。在其符合 POSIX 的模式下,它当然会以其符合 POSIX 的模式运行自身,从而遵守调用符合 POSIX 的 shell 的 POSIX 要求。
  • Z shell、Almquist shell 和 Korn shell 运行时会/bin/sh使用插入在其他参数之前的脚本名称作为其第一个命令行参数。 Z shell、Almquist shell 和 Korn shell(试图)通过假设程序/bin/sh是符​​合 POSIX 的 shell 来调用符合 POSIX 的 shell。

答案3

执行的shell是任何一个在命令行中调用的一个或在 shebang 中调用的一个(如果命令行没有指定它)。

因此,版本 1 和 4 将运行sh,版本 2 和 5 可以使用 bash 运行,版本 6 可以使用不是如果您以交互方式使用 sh (和其他一些),请运行。 Bash 确实启动了脚本。克什也。 Zsh 以 sh 开头。

sh如果 bash 链接到 ,则只有那些以 as 启动的才会使用 posix 选项/bin/sh

将此行添加到您的脚本中以检测是否有任何 bash ksh 或 zsh 版本正在运行它:

echo "Hello World $BASH_VERSION $KSH_VERSION $ZSH_VERSION"

相关内容