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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3664225/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 22:35:55  来源:igfitidea点击:

Determining whether shell script was executed "sourcing" it

bashshell

提问by Sam

Is it possible for a shell script to test whether it was executed through source? That is, for example,

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

$ source myscript.sh
$ ./myscript.sh

Can myscript.shdistinguish from these different shell environments?

myscript.sh能否区分这些不同的shell 环境?

回答by Kurt Pfeifle

I think, what Sam wants to do may be not possible.

我想,山姆想做的事,或许是不可能的。

To what degree a half-baken workaround is possible, depends on...

半生不熟的解决方法在多大程度上是可能的,取决于......

  • ...the default shell of users, and
  • ...which alternative shells they are allowed to use.
  • ...用户的默认外壳,以及
  • ...他们允许使用哪些替代外壳。

If I understand Sam's requirement correctly, he wants to have a 'script', myscript, that is...

如果我正确理解 Sam 的要求,他想要一个“脚本” myscript,即...

  1. ...not directlyexecutable via invoking it by its name myscript(i.e. that has chmod a-x);
  2. ...not indirectlyexecutable for users by invoking sh myscriptor invoking bash myscript
  3. ...onlyrunning its contained functions and commands if invoked by sourcing it: . myscript
  1. ...不能通过按名称调用它直接执行myscript(即具有chmod a-x);
  2. ...不能通过调用或调用对用户间接执行sh myscriptbash myscript
  3. ...如果通过源调用它,则运行其包含的函数和命令:. myscript

The first things to consider are these

首先要考虑的是这些

  1. Invoking a script directly by its name (myscript) requires a first line in the script like #!/bin/bashor similar. This will directly determine which installed instance of the bash executable (or symlink) will be invoked to run the script's content. This will be a new shell process. It requires the scriptfile itself to have the executable flag set.
  2. Running a script by invoking a shell binary with the script's (path+)name as an argument (sh myscript), is the same as '1.' -- except that the executable flag does not need to be set, and said first line with the hashbang isn't required either. The only thing needed is that the invoking user needs readaccess to the scriptfile.
  3. Invoking a script by sourcing its filename (. myscript) is very much the same as '1.' -- exept that it isn't a new shell that is invoked. All the script's commands are executed in the current shell, using its environment (and also "polluting" its environment with any (new) variables it may set or change. (Usually this is a very dangerous thing to do: but here it could be used to execute exit $RETURNVALUEunder certain conditions....)
  1. 直接按名称 ( myscript)调用脚本需要在脚本中使用类似#!/bin/bash或类似的第一行。这将直接确定将调用哪个 bash 可执行文件(或符号链接)的已安装实例来运行脚本的内容。这将是一个新的 shell 进程。它要求脚本文件本身设置可执行标志。
  2. 通过使用脚本的(路径+)名称作为参数 ( sh myscript)调用 shell 二进制文件来运行脚本,与“1”相同。--除了不需要设置可执行标志,也不需要带有 hashbang 的第一行。唯一需要的是调用用户需要对脚本文件的读取访问权限。
  3. 通过获取其文件名 ( . myscript) 来调用脚本与“1”非常相似。--除非它不是被调用的新 shell。所有脚本的命令都在当前 shell 中执行,使用其环境(并且还使用它可能设置或更改的任何(新)变量“污染”其环境。通常这是一件非常危险的事情:但在这里它可能是用于exit $RETURNVALUE在特定条件下执行....)

For '1.':
Easy to achieve: chmod a-x myscriptwill prevent myscriptfrom being directly executable. But this will not fullfill requirements '2.' and '3.'.

对于'1.':
容易实现:chmod a-x myscript将阻止myscript被直接执行。但这不会满足要求“2”。和'3.'。

For '2.' and '3.':
Much harder to achieve. Invokations by sh myscriptrequire readingprivileges for the file. So an obvious way out would seem to chmod a-r myscript. However, this will also dis-allow '3.': you will not be able to source the script either.

对于“2”。和“3.”:
更难实现。调用sh myscript需要 文件的读取权限。所以一个明显的出路似乎是chmod a-r myscript。但是,这也将禁止“3.”:您也将无法获取脚本的来源。

So what about writting the script in a way that uses a Bashism? A Bashism is a specific way to do something which other shells do not understand: using specific variables, commands etc. This could be used inside the script to discover this condition and "do something" about it (like "display warning.txt", "mailto admin" etc.). But there is no way in hell that this will prevent shor bashor any other shell from reading and trying to execute all the following commands/lines written into the script unless you kill the shell by invoking exit.

那么以使用Bashism的方式编写脚本呢?Bashism 是一种执行其他 shell 无法理解的特定方法:使用特定变量、命令等。这可以在脚本内部使用以发现这种情况并对其“做某事”(例如“显示警告.txt”, “mailto admin”等)。但是,有没有办法在地狱,这会阻止shbash或读书,并努力除非你通过调用杀死shell执行写入脚本中的所有下面的命令/行任何其他外壳 exit

Examples:in Bash, the environment seen by the script knows of $BASH, $BASH_ARGV, $BASH_COMMAND, $BASH_SUBSHELL, BASH_EXECUTION_STRING... . If invoked by sh(also if sourcedinside a sh), the executing shell will see all these $BASH_*as empty environment variables. Again, this could be used inside the script to discover this condition and "do something"... but not prevent the following commands from being invoked!

示例:在 Bash 中,脚本看到的环境知道$BASH, $BASH_ARGV, $BASH_COMMAND, $BASH_SUBSHELL, BASH_EXECUTION_STRING... 。如果引用sh(如果也源自内部的sh),执行shell会看到所有这些$BASH_*为空的环境变量。同样,这可以在脚本中使用以发现这种情况并“做某事”……但不能阻止调用以下命令!

I'm now assuming that...

我现在假设...

  1. ...the script is using #!/bin/bashas its first line,
  2. ...users have set Bash as their shell and are invoking commands in the following table from Bash and it is their login shell,
  3. ...shis available and it is a symlink to bashor dash.
  1. ...脚本#!/bin/bash用作第一行,
  2. ...用户已将 Bash 设置为他们的 shell 并从 Bash 调用下表中的命令,这是他们的登录 shell
  3. ...sh是可用的,它是到bash或的符号链接dash

This will mean the following invokations are possible, with the listed values for environment variables

这意味着以下调用是可能的,其中列出了环境变量的值

vars+invok's   | ./scriptname | sh scriptname | bash scriptname | . scriptname
---------------+--------------+---------------+-----------------+-------------
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"
#!/bin/echo Should be run as: source
export SOMEPATH="/some/path/on/my/system"
echo "Your environment has been set up"
" = x"-bash" && echo "$? : FINE.... You've sourced me, and I'm your login shell." test x"
$ ./myscript.sh
Should be run as: source ./myscript.sh

$ source ./myscript.sh
Your environment has been set up
" = 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"
[[ ${BASH_SOURCE[0]} = 
if [ "${0##/*}" == scriptname ]  # if the current name is our script
then
    echo run
else
    echo sourced
fi
]] && main "$@"
" = x"-bash" || exit 33
| ./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)

Now you could put a logic into your text script:

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

  • If $0is not equal to -bash, then do an exit $SOMERETURNVALUE.
  • 如果$0不等于-bash,则执行exit $SOMERETURNVALUE

In case the script was called via sh myscriptor bash myscript, then it will exit the calling shell. In case it was run in the current shell, it will continue to run. (Warning: in case the script has any other exitstatements, your current shell will be 'killed'...)

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

So put into your non-executable myscript.txtnear its beginning something like this may do something close to your goal:

因此,在myscript.txt接近开始时放入您的不可执行文件中,这样的事情可能会接近您的目标:

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

回答by Juan A. Navarro

This may or may not be what the asker wanted but, on a similar situation, I wanted a script to indicate that it is meant to be sourced and not directly run.

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

To achieve this effect my script reads:

为了达到这个效果,我的脚本如下:

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

So when I run it either as a command or sourced I get:

因此,当我将它作为命令或来源运行时,我得到:

#! /bin/bash                                                                                       

sourced () {
    echo Sourced
}

executed () {
    echo Executed
}

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

You can of course fool the script by running it as sh ./myscript.sh, but at least it gives the correct expected behaviour on 2 out of 3 cases.

您当然可以通过将脚本作为 运行来欺骗脚本sh ./myscript.sh,但至少它在 3 种情况中的 2 种情况下给出了正确的预期行为。

回答by Karsten

This is what I was looking for:

这就是我要找的:

$ ./myscript
Executed
$ . ./myscript
Sourced

回答by Josuah Demangeon

I cannot add comment yet (stackexchange policies) so I add my own answer:

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

This one may works regardless if we do:

无论我们是否这样做,这个方法都可能有效:

  • bash scriptname
  • scriptname
  • ./scriptname.
  • bash scriptname
  • scriptname
  • ./scriptname.

on both bashand mksh.

bash和 上mksh

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

回答by Nelson Marcos

Since all of our machines have history, I did this:

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

##代码##

Everytime you run a script (without source), your shell creates a new env without history.

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

If you want to care about performance you can try this:

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

##代码##

PS: I think Kurt's answer is much more complete but I think this could help.

PS:我认为库尔特的回答要完整得多,但我认为这会有所帮助。

回答by SourceSimian

Yes it is possible. In general you can do the following:

对的,这是可能的。一般来说,您可以执行以下操作:

##代码##

Giving the following output:

给出以下输出:

##代码##

回答by Daniel Andersson

If you have a non-altering file path for regular users, then:

如果您对普通用户有一个不可更改的文件路径,则:

##代码##

(it can also easily be loosened to only check for the filename or whatever).

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

But your users need to have read permission to be able to source the file, so absolutely nothingcan stop them from doing what they want with the file. But it might help them out to not use it in the wrong way.

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

This solution is not dependent on Bashisms.

此解决方案不依赖于 Bashisms。

回答by Steven Penny

Based on Kurt Pfeifle's answer, this works for me

根据Kurt Pfeifle 的回答,这对我有用

##代码##

Example

例子

回答by Borealid

In the first case, $0will be "myscript.sh". In the second case, it will be "./myscript". But, in general, there's no way to tell sourcewas used.

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

If you tell us what you're trying to do, instead of how you want to do it, a better answer might be forthcoming.

如果您告诉我们您正在尝试做什么,而不是您想如何做,那么可能会有更好的答案。