确定shell脚本是否被执行“采购”它

时间:2010-09-08 02:24:41

标签: bash shell

shell脚本是否可以测试它是否是通过source执行的?也就是说,例如,

$ source myscript.sh
$ ./myscript.sh

myscript.sh 可以区别于这些不同的shell环境吗?

9 个答案:

答案 0 :(得分:41)

我认为,山姆想要做的事可能是不可能的。

半假的解决办法在多大程度上取决于......

  • ...默认的用户shell和
  • ......允许他们使用哪种替代炮弹。

如果我理解Sam的要求,他想要一个'脚本', myscript,就是......

  1. ... 直接可执行文件,通过名称myscript调用它 (即有chmod a-x);
  2. 通过调用sh myscript或者
  3. ... 间接可执行文件 调用bash myscript
  4. ... 运行其包含的函数和命令(如果由其调用) 采购它:. myscript
  5. 首先要考虑的是这些

    1. 直接通过名称(myscript)调用脚本需要第一行 像#!/bin/bash或类似的脚本。这将直接决定哪个 将调用已安装的bash可执行文件(或符号链接)实例来运行 脚本的内容。这将是一个新的shell进程。它需要 scriptfile本身设置了可执行标志。
    2. 通过使用脚本的(路径+)名称调用shell二进制文件来运行脚本 参数(sh myscript)与“1”相同 - 除了 可执行标志不需要设置,并表示第一行 不需要hashbang 。唯一需要的是调用 用户需要读取访问脚本文件。
    3. 通过获取文件名(. myscript)来调用脚本非常重要 与'1'相同 - 表示它不是一个被调用的新shell。一切 脚本的命令在当前shell中执行 ,使用其环境 (并且还用它可能设置的任何(新)变量“污染”其环境或 更改。 (通常这是一件非常危险的事情:但这可能是 用于在某些条件下执行exit $RETURNVALUE ....)
    4. 对于'1。':
      易于实现:chmod a-x myscript会阻止myscript 可直接执行。但这不会满足要求'2'。和'3。'。

      对于'2'和'3。':
      更难实现。 sh myscript的邀请函要求阅读 该文件的权限。所以chmod a-r myscript似乎有一个显而易见的出路。但是,这也将禁止'3':你将无法做到 找到脚本。

      那么以一种使用 Bashism 的方式编写脚本呢?一个Bashism是一个 做其他shell不理解的事情的具体方法:使用 特定的变量,命令等。这可以在脚本内部使用 发现这种情况并“做点什么”(比如“display warning.txt”, “mailto admin”等)。但是地狱里没有办法阻止sh或者{。}} bash或任何其他shell读取并尝试执行以下所有操作 除非你通过调用杀死shell,否则命令/行写入脚本 exit

      示例:在Bash中,脚本看到的环境知道$BASH$BASH_ARGV$BASH_COMMAND$BASH_SUBSHELLBASH_EXECUTION_STRING ....如果 由sh调用(如果在<{1}}内 sourced ),执行的shell将会看到 所有这些sh都是空的环境变量。同样,这可以使用 在脚本中发现这种情况并“做某事”...... 但不是 阻止调用以下命令!

      我现在假设......

      1. ...脚本使用$BASH_*作为第一行
      2. ...用户已将Bash设置为shell并正在调用命令 来自Bash的下表,它是登录shell
      3. ... #!/bin/bash可用,它是shbash的符号链接。
      4. 这意味着可以使用列出的值进行以下调用 对于环境变量

        vars+invok's   | ./scriptname | sh scriptname | bash scriptname | . scriptname
        ---------------+--------------+---------------+-----------------+-------------
        $0             | ./scriptname | ./scriptname  | ./scriptname    | -bash
        $SHLVL         | 2            | 1             | 2               | 1
        $SHELLOPTS     | braceexpand: | (empty)       | braceexpand:..  | braceexpand:
        $BASH          | /bin/bash    | (empty)       | /bin/bash       | /bin/bash
        $BASH_ARGV     | (empty)      | (empty)       | (empty)         | scriptname
        $BASH_SUBSHELL | 0            | (empty)       | 0               | 0
        $SHELL         | /bin/bash    | /bin/bash     | /bin/bash       | /bin/bash
        $OPTARG        | (empty)      | (empty)       | (emtpy)         | (emtpy)
        

        现在您可以在文本脚本中加入逻辑:

        • 如果dash不等于$0,请执行-bash

        如果脚本是通过exit $SOMERETURNVALUEsh myscript调用的,那么它会 退出调用shell。如果它在当前shell中运行,它将会 继续跑。 (警告:如果脚本有任何其他bash myscript语句, 你当前的shell将被“杀死”......)

        所以把它放在你的非可执行文件exit附近 这可能会接近你的目标:

        myscript.txt

答案 1 :(得分:15)

这可能是也可能不是提问者想要的,但在类似的情况下,我想要一个脚本来表明它是来源而不是直接运行。

要实现此效果,我的脚本会显示:

#!/bin/echo Should be run as: source
export SOMEPATH="/some/path/on/my/system"
echo "Your environment has been set up"

因此,当我以命令或来源的方式运行它时,我得到:

$ ./myscript.sh
Should be run as: source ./myscript.sh

$ source ./myscript.sh
Your environment has been set up

你当然可以通过运行sh ./myscript.sh来欺骗脚本,但至少它会在3个案例中有2个给出正确的预期行为。

答案 2 :(得分:2)

这就是我想要的:

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"

答案 3 :(得分:1)

如果普通用户有不改变的文件路径,那么:

if [ "$(/bin/readlink -f "$0")" = "$KNOWN_PATH_OF_THIS_FILE" ]; then
    # the file was executed
else
    # the file was sourced
fi

(它也可以很容易地放松,只检查文件名或其他)。

但是您的用户需要具有读取权限才能获取文件,因此绝对 nothing 可以阻止他们对文件执行他们想要的操作。但它可能会帮助他们不以错误的方式使用它。

此解决方案不依赖于Bashisms。

答案 4 :(得分:1)

基于Kurt Pfeifle’s answer,这对我有用

if [ $SHLVL = 1 ]
then
  echo 'script was sourced'      
fi

Example

答案 5 :(得分:1)

是的,这是可能的。通常,您可以执行以下操作:

#! /bin/bash                                                                                       

sourced () {
    echo Sourced
}

executed () {
    echo Executed
}

if [[ ${0##*/} == -* ]]; then
    sourced
else
    executed $@
fi

提供以下输出:

$ ./myscript
Executed
$ . ./myscript
Sourced

答案 6 :(得分:1)

由于我们所有的机器都有历史,我这样做了:

check_script_call=$(history |tail -1|grep myscript.sh )
if [ -z "$check_script_call" ];then
    echo "This file should be called as a source."
    echo "Please, try again this way:"
    echo "$ source /path/to/myscript.sh"
    exit 1
fi

每次运行脚本(没有源代码)时,shell都会创建一个没有历史记录的新环境。

如果您想关心性能,可以试试这个:

if ! history |tail -1|grep set_vars ;then
    echo -e "This file should be called as a source.\n"
    echo "Please, try again this way:"
    echo -e "$ source /path/to/set_vars\n"
    exit 1
fi
PS:我认为Kurt的答案要完整得多,但我认为这可能会有所帮助。

答案 7 :(得分:1)

我还无法添加评论(stackexchange政策)所以我添加了自己的答案:

如果我们这样做,这个可能会有效:

  • bash scriptname
  • scriptname
  • ./scriptname

bashmksh

if [ "${0##/*}" == scriptname ]  # if the current name is our script
then
    echo run
else
    echo sourced
fi

答案 8 :(得分:0)

在第一种情况下,$0将是“myscript.sh”。在第二种情况下,它将是“./myscript”。但是,一般来说,没有办法告诉source被使用过。

如果你告诉我们你想要做什么,而不是你想做什么,可能会有更好的答案。