shell脚本是否可以测试它是否是通过source
执行的?也就是说,例如,
$ source myscript.sh
$ ./myscript.sh
myscript.sh 可以区别于这些不同的shell环境吗?
答案 0 :(得分:41)
我认为,山姆想要做的事可能是不可能的。
半假的解决办法在多大程度上取决于......
如果我理解Sam的要求,他想要一个'脚本',
myscript
,就是......
myscript
调用它
(即有chmod a-x
); sh myscript
或者bash myscript
. myscript
首先要考虑的是这些
myscript
)调用脚本需要第一行
像#!/bin/bash
或类似的脚本。这将直接决定哪个
将调用已安装的bash可执行文件(或符号链接)实例来运行
脚本的内容。这将是一个新的shell进程。它需要
scriptfile本身设置了可执行标志。sh myscript
)与“1”相同 - 除了
可执行标志不需要设置,并表示第一行
不需要hashbang 。唯一需要的是调用
用户需要读取访问脚本文件。. myscript
)来调用脚本非常重要
与'1'相同 - 表示它不是一个被调用的新shell。一切
脚本的命令在当前shell中执行 ,使用其环境
(并且还用它可能设置的任何(新)变量“污染”其环境或
更改。 (通常这是一件非常危险的事情:但这可能是
用于在某些条件下执行exit $RETURNVALUE
....) 对于'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_SUBSHELL
,BASH_EXECUTION_STRING
....如果
由sh
调用(如果在<{1}}内 sourced ),执行的shell将会看到
所有这些sh
都是空的环境变量。同样,这可以使用
在脚本中发现这种情况并“做某事”...... 但不是
阻止调用以下命令!
我现在假设......
$BASH_*
作为第一行#!/bin/bash
可用,它是sh
或bash
的符号链接。这意味着可以使用列出的值进行以下调用 对于环境变量
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 $SOMERETURNVALUE
或sh 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)
答案 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
。 bash
和mksh
。
if [ "${0##/*}" == scriptname ] # if the current name is our script
then
echo run
else
echo sourced
fi
答案 8 :(得分:0)
在第一种情况下,$0
将是“myscript.sh”。在第二种情况下,它将是“./myscript”。但是,一般来说,没有办法告诉source
被使用过。
如果你告诉我们你想要做什么,而不是你想做什么,可能会有更好的答案。