如何定义不运行的shell脚本

alg*_*gal 48 shell bash

我正在定义一个用户应该source而不是执行的 shell 脚本。

是否有一种传统的或智能的方式来提示用户这是这种情况,例如通过文件扩展名?

是否有我可以在文件本身中编写的 shell 代码,如果它被执行而不是源代码,这将导致它回显消息并退出,以便我可以帮助用户避免这个明显的错误?

Joh*_*024 54

假设您正在运行 bash,请将以下代码放在您想要获取但不执行的脚本的开头附近:

if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
    echo "Hey, you should source this script, not execute it!"
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)

在 bash 下,${BASH_SOURCE[0]}将包含 shell 正在读取的当前文件的名称,而不管它是源文件还是执行文件。

相比之下,$0是正在执行的当前文件的名称。

-ef测试这两个文件是否是同一个文件。如果是,我们会提醒用户并退出。

POSIX既不是,-ef也不BASH_SOURCE是。虽然-ef由 ksh、yash、zsh 和 Dash 支持,但BASH_SOURCE需要 bash。 zsh,然而,${BASH_SOURCE[0]}可能被替换${(%):-%N}

  • @kubanczyk `source` 不可移植。考虑到这个答案是特定于 bash 的,还不错,但是使用便携式 `.` 是一个好习惯 (7认同)
  • 只需`echo "Usage: source \"$myfile\""` (4认同)

xen*_*oid 36

一个不可执行的文件可以被获取但不能被执行,因此,作为第一道防线,不设置可执行标志应该是一个很好的提示......

编辑:我刚刚偶然发现的技巧:使shebang成为不是shell解释器的任何可执行文件,/bin/false使脚本返回错误(rc!= 0)

#!/bin/false "This script should be sourced in a shell, not executed directly"
Run Code Online (Sandbox Code Playgroud)

  • 然而,一个不可执行的文件仍然可以通过例如`bash somefile.sh` 来执行... (8认同)
  • 如果您知道可以使用“bash”(vd perl、python、awk...)执行它,那么您已经查看了源代码并看到了不这样做的评论:) (2认同)
  • Perl 脚本通常被命名为“somefile.pl”,Python 被命名为“somefile.py”,所以不,我可能还没有阅读注释(那些是什么,甚至,无论如何?)并且“bash somefile.sh”更短输入`chmod +x somefile.sh; ./somefile.sh`... (2认同)

mur*_*uru 11

这篇 Stack Overflow 帖子中建议了几种方法,其中,我最喜欢Wirawan Purwantomr.spuratic建议的基于函数的方法:

正如 Wirawan Purwanto 所建议的,最可靠的方法是FUNCNAME[1] 在函数内进行检查 :

function mycheck() { declare -p FUNCNAME; }
mycheck
Run Code Online (Sandbox Code Playgroud)

然后:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'
Run Code Online (Sandbox Code Playgroud)

这相当于检查 的输出caller、值 mainsource区分调用者的上下文。使用 FUNCNAME[]可以保存您捕获和解析caller输出。不过,您需要知道或计算本地呼叫深度才能正确。像脚本来自另一个函数或脚本的情况会导致数组(堆栈)更深。(FUNCNAME是一个特殊的 bash 数组变量,它应该有对应于调用堆栈的连续索引,只要它从不unset。)

所以你可以添加到脚本的开头:

function check()
{
    if [[ ${FUNCNAME[-1]} != "source" ]]   # bash 4.2+, use ${FUNCNAME[@]: -1} for older
    then
        printf "Usage: source %s\n" "$0"
        exit 1
    fi
}
check
Run Code Online (Sandbox Code Playgroud)


che*_*ner 7

假设执行脚本只是无用而不是有害,您可以添加

return 0 || printf 'Must be sourced, not executed\n' >&2
Run Code Online (Sandbox Code Playgroud)

到脚本的结尾return除非该文件是源文件,否则函数外部具有非零退出代码。

  • 请注意,这将返回 0 退出状态。试试我用的这个类似的成语:`return 2>/dev/null; echo "$0: 这个脚本必须是源代码" 1>&2; 出口1` (3认同)

Ing*_*kat 5

当您获取 shell 脚本时,shebang行将被忽略。通过放入无效的shebang,您可以提醒用户脚本被错误执行:

#!/bin/bash source-this-script
# ...
Run Code Online (Sandbox Code Playgroud)

错误消息将是这样的:

/bin/bash: source-this-script: No such file or directory
Run Code Online (Sandbox Code Playgroud)

(任意)参数名称已经提供了一个强有力的提示,但错误消息仍然不是 100% 清楚。我们可以使用source-this-script放置在您的某个位置的实用程序脚本来解决此问题PATH

#!/bin/sh
echo >&2 "This script must be sourced, not executed${1:+: }${1:-!}"
exit 1
Run Code Online (Sandbox Code Playgroud)

现在,错误消息将是这样的:

This script must be sourced, not executed: path/to/script.sh
Run Code Online (Sandbox Code Playgroud)

与其他方法的比较

与其他答案相比,这种方法只需要对每个脚本进行最少的更改(并且使用 shebang 行有助于在编辑器中检测文件类型并指定 shell 脚本方言,因此甚至有好处)。缺点是错误消息有点不清楚,或者(一次)添加了另一个 shell 脚本。

它不会通过以下方式阻止显式调用 bash path/to/script.sh(感谢@muru!)。

  • 您可以通过使shebang `#!/bin/echo 'You must source this script!'` 或类似内容使信息更清晰。 (4认同)
  • 另一个缺点是这不会防止`bash some/script.sh`,它也会忽略shebang。 (2认同)