区分运行和bash shell脚本中的源代码?


22

我在这里要问的是非常非传统/非常规/危险的,或者我的Google-fu技能根本无法解决...

bashshell脚本中,是否有任何简单的方法可以判断它是从另一个shell脚本获取资源,还是由它自己运行?换句话说,是否可以区分以下两种行为?

# from another shell script
source myScript.sh

# from command prompt, or another shell script
./myScript.sh

我正在考虑做的是创建一个类似于实用程序的shell脚本,其中包含bash可以在获取源代码后使用的功能。当这个脚本自己运行时,我也会希望它根据定义的功能执行某些操作。这个shell脚本是否可以使用某种环境变量,例如

some_function() {
    # ...
}
if [ -z "$IS_SOURCED" ]; then
    some_function;
fi

最好是,我正在寻找一种不需要调用者脚本设置任何标志变量的解决方案。

编辑:我知道在源代码和运行脚本之间的区别,我试图在这里找到什么,才能分辨出正在使用的脚本的区别(两种方式)。



@cuonglm编辑了我的问题,我知道两者之间的区别,但是我想知道是否可以以编程方式使Shell脚本也能分辨出区别。
hjk 2015年

4
@cuonglm:不是重复的。他根本不是在问.命令,而只是在检测脚本是否是源代码或正常运行(即在子外壳中)。
詹德,2015年

关于堆栈溢出的相同问题的很好答案:stackoverflow.com/a/28776166/96944
Jannie Theunissen

Answers:


19

是的-$ 0变量给出了脚本的名称:

$ cat example.sh
#!/bin/bash
script_name=$( basename ${0#-} ) #- needed if sourced no path
this_script=$( basename ${BASH_SOURCE} )
if [[ ${script_name} = ${this_script} ]] ; then
    echo "running me directly"
else
    echo "sourced from ${script_name}"
fi 

$ cat example2.sh
#!/bin/bash
. ./example.sh

运行方式如下:

$ ./example.sh
running me directly
$ ./example2.sh
example.sh sourced from example2.sh

不能满足于从交互式外壳中获取源代码,但是您有这个想法(我希望如此)。

已更新为包括BASH_SOURCE-谢谢hjk


足够近。:)我至少需要指定脚本名称,但是如果没有其他可行的解决方案,我将采用这个答案……
hjk 2015年

7

@DarkHeart的答案与环境变量结合起来BASH_SOURCE似乎可以解决问题:

$ head example*.sh
==> example2.sh <==
#!/bin/bash
. ./example.sh

==> example.sh <==
#!/bin/bash
if [ "$(basename $0)" = "$(basename $BASH_SOURCE)" ]; then
    echo "running directly"
else
    echo "sourced from $0"
fi
$ ./example2.sh
sourced from ./example2.sh
$ ./example.sh
running directly

编辑似乎是一个简单的解决方案仍然如果我算了算中的元素个数BASH_SOURCE的数组:

if [ ${#BASH_SOURCE[@]} -eq 1 ]; then echo "running directly"; else echo "sourced from $0"; fi

1
看起来我们同时找到了'bash_source'变量。:)
DarkHeart 2015年

@DarkHeart我也添加了对数组大小计数的用法...感谢您在这条路上向我暗示!:D
hjk 2015年

1

我刚刚创建了与BusyBox类似的库脚本。在其中,我使用以下函数来测试它是否是来源...

function isSourced () {
  [[ "${FUNCNAME[1]}" == "source" ]]  && return 0
  return 1
}

Bash维护的FUNCNAME数组本质上是一个函数调用堆栈。$FUNCNAME(或${FUNCNAME[0]})是当前正在执行的函数的名称。${FUNCNAME[1]}是调用它的函数的名称,依此类推。

最顶层的项是脚本本身的特殊值。它将包含...

  • 单词“ source”(如果源脚本)
  • 如果正在执行脚本且正在通过函数进行测试,则为“ main”一词
  • “”(空),如果脚本正在执行并且测试在任何功能之外进行,即……在脚本本身的级别。

上面的函数实际上仅在脚本级别调用时才起作用(这是我所需要的)。如果从另一个函数内部调用它将失败,因为数组项号将是错误的。为了使它在任何地方都可以工作,需要找到栈顶并测试该值,这更加复杂。

如果需要,可以使用以下命令获取“堆栈顶部”的阵列项目编号:

  local _top_of_stack=$(( ${#FUNCNAME[@]} - 1 ))

${#FUNCNAME[@]}是数组中的项目数。作为从零开始的数组,我们减去1得到最后一个项目。

这三个函数一起使用以产生类似于Python的函数堆栈跟踪,它们可能使您更好地了解所有这些工作原理...

function inspFnStack () {
  local T+="  "
  local _at=
  local _text="\n"
  local _top=$(inspFnStackTop)
  local _fn=${FUNCNAME[1]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local i=_top; ((--i))
  #
  _text+="$i item function call stack for $_fn ...\n"
  _text+="| L   BASH_SOURCE{BASH_LINENO called from}.FUNCNAME  \n"
  _text+="| ---------------------------------------------------\n"
  while (( $i > 0 ))
  do
    _text+="| $i ${T}$(inspFnStackItem $i)\n"
    T+="  "
    ((--i))
  done
  #
  printf "$_text\n"
  #
  return 0
}

function inspFnStackItem ()  {
  local _i=$1
  local _fn=${FUNCNAME[$_i]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local _at="${BASH_LINENO[$_i-1]}"; [[ $_at == 1 ]]  && _at="trap"
  local _item="${BASH_SOURCE[$_i]}{${_at}}.$_fn"
  #
  printf "%s" "$_item"
  return 0
}

function inspFnStackTop () {
  # top stack item is 1 less than length of FUNCNAME array stack
  printf "%d\n" $(( ${#FUNCNAME[@]} - 1 ))
  #
  return 0
}

请注意,FUNCNAME,BASH_SOURCE和BASH_LINENO是bash维护的3个数组,就好像它们是一个三维数组一样。


0

只是想补充一点,对数组的计数似乎是不可靠的,可能不应该source使用该数组,因为使用点(.)也是很常见的(并且早于source关键字)。

例如,对于sourced.sh仅包含的脚本echo $0


$ . sourced.sh 
bash
$ source sourced.sh 
bash
$ chmod +x sourced.sh 
$ ./sourced.sh 
./sourced.sh
$ cat ./sourced.sh 
echo $0

比较解决方案建议更好地工作。


0

交互式shell进行采购时,一种也可行的方法:

if [ $BASH_LINENO -ne 0 ]; then
    some_function;
fi

BASH_LINENO变量也是一个数组,其中包含执行调用函数的所有行。如果直接调用脚本,则该值为零,或者对应于行号的整数。

BASH_ *变量文档

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.