检测脚本是否来自“/bin/sh”

Detect if a script has been sourced in "/bin/sh"

我在这里找到的大多数答案似乎只适用于 /bin/bash

$BASH_SOURCE$SHLVL 等技巧似乎不适用于 sh

有一个答案要求使用 return,因为它只适用于函数和源脚本,看看它是否产生任何错误,但我不明白为什么执行 return在命令行上,我退出了 shell。如果我“执行或获取”包含 return 的脚本,它只会退出该脚本。这是我在 freebsd 时发生的事情。我也不在那里使用任何桌面环境。

只需在命令行输入,

return

result: logged out

正在执行或获取包含 return:

的脚本
$ cat testscript
#! /bin/sh

echo hello
return
echo hello
$ ./testscript
hello
$ . testscript
hello
$ 

当我在 macOS 上执行相同操作时情况并非如此(首先执行 /bin/sh)。它在那里工作得很好。那里只是说

sh: return: can only `return' from a function or sourced script

符合预期。

我正在寻找一种解决方案来检测脚本是否来自 /bin/sh

我正在使用 freebsd,目前我将默认 shell 设置为 sh。我知道我可以安装 bash,但我仍然想知道如何为 /bin/sh 做同样的事情。

更新:

我想再说一点细节。

MacOS

macOS中我尝试通过命令行启动/bin/sh,后来才知道是非登录shell。所以,当我在那里输入 logout 时,结果是:

sh: logout: not login shell: use `exit'

所以我将 /bin/sh 作为我的默认值 shell 并且我确信 /bin/sh 已被执行。当我在那里输入 return 时,我得到的输出是:

sh: return: can only `return' from a function or sourced script

果然不出所料。但是当我输入 echo $SHELL 时,输出是:

/bin/bash

而且我检查了我机器的 /bin 目录,/bin/sh/bin/bash 似乎没有链接。

FreeBSD

现在我也尝试在那里执行 /bin/sh。结果如下:

$ /bin/sh
$ return
$ return
logged out on 2nd return

所以用简单的语言来说,如果 /bin/sh 是非登录 shell 并且只是退出 shell.

则它不会显示任何输出

@user1934428 在@CharlesDuffy 的回答中提供了一些不错的信息。值得一读。

他提到 FreeBSD 手册没有 return 语句的文档。 sh man page, FreeBSD

我检查了 OpenBSD 是否有相同的手册页大小写,但它确实将 return 定义为:

return [n] Exit the current function or . script with exit status n, or that of the last command executed.

sh man page, OpenBSD

另一个问题是大多数手册页显示 bash 手册要求 man sh。我不知道它是否应该是那样的。

此外,有人可以建议我是否应该针对 return 的未定义行为开始一个新问题吗?因为我觉得这个问题真的跑题了。不确定这样做是否是个好主意。

$ sh ./detect-sourcing.sh
We were executed
$ sh -c '. ./detect-sourcing.sh'
We were sourced
#!/bin/sh
if (return 2>/dev/null); then
  echo "We were sourced"
else
  echo "We were executed"
fi

我没有分析这是否是 POSIX sh 标准的严格要求,但它适用于 MacOS 上的 /bin/sh,OP 的 .

我通过 FreeBSD mailing lists 找到了这个问题的答案。

缺少 return 条目的手册页是错误的手册页。

查看正确的 man page,已说明 return 语句的完整行为。

The syntax  of the return command is

       return [exitstatus]

     It terminates the current executional scope, returning from the closest
     nested function or sourced script; if no function or sourced script is
     being executed, it exits the shell instance.  The return command is im-
     plemented as a special built-in command.

正如 Ian 在邮件列表中所建议的那样,在 /bin/sh 的情况下,一个好的可能方法似乎是为您的脚本保留一个固定名称并扩展 [=15=]:

${0##*/}

并将其与名称匹配。如果扩展产生任何其他内容,则意味着脚本已被获取。另一种情况可能是用户重命名了它。所以它不是完全容易出错,但仍然应该完成我的工作。

如果您打算使用 Bourne shell (/bin/sh),只测试 $0 效果很好。

$ cat t
#!/bin/sh

if [ [=10=] == "sh" ]; then
    echo "sourced"
else
    echo executed
fi

$ . t
sourced
$ . ./t
sourced
$ ./t
executed
$ sh t
executed
$ sh ./t
executed

如果您想从其他 shell 调用或获取脚本,请根据 shell 名称列表测试 $0。

正如@Mihir 指出的那样,FreeBSD shell 按照手册页 sh(1) 中的描述工作。 MacOS /bin/sh 基本上是 bash 尽管文件 /bin/sh 和 /bin/bash 略有不同。 请注意 Mac 上的命令 man sh 为 bash

带来手册页