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

Sam*_*Sam 28 bash shell

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

$ source myscript.sh
$ ./myscript.sh
Run Code Online (Sandbox Code Playgroud)

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

Kur*_*fle 40

我想,Sam想要做的事可能是不可能的.

一个半假的解决方法可以在多大程度上取决于......

  • ...用户的默认shell,和
  • ......他们可以使用哪种替代炮弹.

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

  1. ...不能通过名称调用它直接执行myscript (即具有chmod a-x);
  2. ...不能通过调用间接对用户执行sh myscript或调用bash myscript
  3. ... 在通过采购来调用时才运行其包含的函数和命令:. myscript

首先要考虑的是这些

  1. 直接通过名称(myscript)调用脚本需要脚本中的第一行,如#!/bin/bash或类似.这将直接确定将调用哪个已安装的bash可执行文件(或符号链接)实例来运行脚本的内容.这将是一个新的shell进程.它要求脚本文件本身设置可执行标志.
  2. 通过使用脚本的(路径+)名称作为参数(sh myscript)调用shell二进制文件来运行脚本,与"1"相同.- 除了不需要设置可执行标志,并且不需要使用hashbang表示第一行.唯一需要的是调用用户需要对脚本文件的访问权.
  3. 通过获取文件名(. 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"等).但是,除非你通过调用杀死shell,否则无法阻止sh或者 bash任何其他shell读取并尝试执行写入脚本的所有以下命令/行 exit.

例如:在Bash中,通过脚本知道看到的环境$BASH, $BASH_ARGV,$BASH_COMMAND,$BASH_SUBSHELL,BASH_EXECUTION_STRING....如果由sh(在a中来源sh)调用,则执行的shell将把所有这些$BASH_*视为空的环境变量.同样,这可以在脚本中用于发现这种情况并"做某事"...... 但不能阻止调用以下命令!

我现在假设......

  1. ...脚本正在使用#!/bin/bash它的第一行,
  2. ...用户已将Bash设置为shell并从Bash调用下表中的命令,它是他们的登录shell,
  3. ... sh是可用的,它是bash或的符号链接dash.

这意味着可以使用列出的环境变量值进行以下调用

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)

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

  • 如果$0不相等-bash,那就做一个exit $SOMERETURNVALUE.

如果脚本是通过sh myscriptbash myscript调用的,那么它将退出调用shell.如果它在当前shell中运行,它将继续运行.(警告:如果脚本有任何其他exit语句,您当前的shell将被"杀死"......)

所以myscript.txt在它的开头附近放入你的非可执行文件这样的东西可能会做一些接近你的目标:

echo BASH=$BASH
test x${BASH} = x/bin/bash && echo "$? :    FINE.... You're using 'bash ...'"
test x${BASH} = x/bin/bash || echo "$? :    RATS !!! -- You're not using BASH and I will kick you out!"
test x${BASH} = x/bin/bash || exit 42
test x"${0}" = x"-bash"    && echo "$? :    FINE.... You've sourced me, and I'm your login shell."
test x"${0}" = x"-bash"    || echo "$? :    RATS !!! -- You've not sourced me (or I'm not your bash login shell) and I will kick you out!"
test x"${0}" = x"-bash"    || exit 33
Run Code Online (Sandbox Code Playgroud)

  • 很棒的答案.我希望我能给你的不只是一个upvote. (6认同)
  • 谢谢你彻底的分析 (2认同)

Jua*_*rro 15

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

为了达到这个效果,我的脚本读取:

#!/bin/echo Should be run as: source
export SOMEPATH="/some/path/on/my/system"
echo "Your environment has been set up"
Run Code Online (Sandbox Code Playgroud)

因此,当我作为命令或源代码运行时,我得到:

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

$ source ./myscript.sh
Your environment has been set up
Run Code Online (Sandbox Code Playgroud)

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