我有一个脚本,我不希望它调用退出,如果它是来源。

我想检查是否$0 == bash,但这有问题,如果脚本是从另一个脚本,或者如果用户从不同的shell,如ksh源。

是否有一种可靠的方法来检测脚本是否被引用?


当前回答

我遵循mklement0紧凑表达式。

这很整洁,但我注意到,当调用ksh时,它可能会失败:

/bin/ksh -c ./myscript.sh

(它认为它是源的,而不是因为它执行了一个子shell) 但是表达式可以检测到这一点:

/bin/ksh ./myscript.sh

此外,即使表达式是紧凑的,语法也不兼容所有shell。

因此,我以以下代码结束,它适用于bash,zsh,dash和ksh

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

请随意添加异国情调的贝壳支持:)

其他回答

我认为在ksh和bash中没有任何可移植的方法来做到这一点。在bash中,您可以使用调用器输出来检测它,但我认为ksh中不存在等效的输出。

我将给出一个特定于bash的答案。Korn shell,对不起。假设脚本名为include2.sh;然后在include2.sh中创建一个名为am_I_sourced的函数。下面是我的include2.sh的演示版本:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

现在尝试以多种方式执行它:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

所以这是毫无例外的工作,它没有使用脆弱的$_东西。这个技巧使用了BASH的自省功能,即内置变量FUNCNAME和BASH_SOURCE;请参阅bash手册页中的文档。

只有两个警告:

1)对am_I_called的调用必须发生在源脚本中,而不是在任何函数中,以免${FUNCNAME[1]}返回其他东西。是的…您本可以检查${FUNCNAME[2]},但这样做只会使您的工作更加困难。

2)函数am_I_called必须驻留在源脚本中,如果你想知道被包含的文件的名称。

编者注:这个答案的解决方案工作稳健,但只有bash。它可以简化为 (返回2 > / dev / null)。

博士TL;

尝试执行return语句。如果脚本没有来源,则会引发错误。您可以捕获该错误并按照需要进行操作。

把它放在一个文件中,并调用它,比如test.sh:

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

直接执行:

shell-prompt> sh test.sh
output: This script is not sourced.

来源:

shell-prompt> source test.sh
output: This script is sourced.

对我来说,这可以在zsh和bash中工作。

解释

如果您试图在函数之外执行return语句,或者如果脚本不是源代码,则return语句将引发错误。在shell提示符中尝试以下操作:

shell-prompt> return
output: ...can only `return` from a function or sourced script

你不需要看到错误消息,所以你可以将输出重定向到dev/null:

shell-prompt> return >/dev/null 2>&1

现在检查逃生码。0表示OK(没有发生错误),1表示发生错误:

shell-prompt> echo $?
output: 1

您还希望在子shell中执行return语句。当return语句运行它时…嗯……的回报。如果在子shell中执行它,它将从子shell返回,而不是从脚本返回。要在子shell中执行,请将其包装在$(…)中:

shell-prompt> $(return >/dev/null 2>$1)

现在,你可以看到子shell的退出代码,它应该是1,因为在子shell内部引发了一个错误:

shell-prompt> echo $?
output: 1

直截了当:您必须计算变量“$0”是否等于Shell的名称。

是这样的:

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

通过壳:

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

通过来源:

$ source check_source.sh
First Parameter: bash

The script was sourced.


很难有一种100%可移植的方法来检测脚本是否来自源代码。

根据我的经验(使用Shellscripting 7年),唯一安全的方法(不依赖具有pid等的环境变量,这是不安全的,因为它是变量),你应该:

扩展你的“如果”的可能性 使用开关/箱子,如果你想。

这两个选项都不能自动缩放,但这是更安全的方式。


例如:

当您通过SSH会话源脚本时,变量“$0”(当使用source时)返回的值是-bash。

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

OR

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi

这在后面的脚本中起作用,不依赖于_变量:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

or

[ $(basename $0) = $Prog ] && exit