我正在寻找“。”和“source”内置命令和一些来源之间的区别(例如,在这讨论,以及狂欢手册页 (manpage) 表明这些是完全相同的。
但是,由于环境变量存在问题,我进行了测试。我创建了一个文件,testenv.sh
其中包含:
#!/bin/bash
echo $MY_VAR
在命令提示符中,我执行以下操作:
> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh
> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345
[请注意,第一个表单返回一个空字符串]
所以这个小实验表明是毕竟是有区别的,对于“source”命令,子环境会从父环境继承所有变量,而对于“。”则不会。
我是否遗漏了什么,或者这是一个未记录/弃用的功能狂欢?
[ GNU bash,版本 4.1.5(1)-发布(x86_64-pc-linux-gnu) ]
答案1
简短答案
在您的问题中,第二个命令既不使用.
shell 内置命令,也不使用source
内置命令。相反,您实际上是运行脚本在单独的 shell 中,通过像调用其他可执行文件一样按名称调用它。这确实为它提供了一组单独的变量(尽管如果你出口一个变量在其父 shell 中,它将成为任何子进程的环境变量,因此将包含在子 shell 的变量中)。如果将 改为/
空格,那么它将使用.
内置的运行它,这相当于source
。
延伸解释
source
这是shell 内置的语法,它在当前 shell 中执行脚本的内容(从而使用当前 shell 的变量):
source testenv.sh
这是内置的语法.
,它的作用与以下内容相同source
:
. testenv.sh
但是,此语法将脚本作为可执行文件运行,并启动新的 shell 来运行它:
./testenv.sh
这不是使用.
内置的。相反,.
是您正在执行的文件路径的一部分。一般来说,您可以通过使用至少包含一个/
字符的名称调用它来运行 shell 中的任何可执行文件。因此,要在当前目录中运行文件,在文件前面加上./
是最简单的方法。除非当前目录在您的 中PATH
,否则您无法使用命令 运行脚本testenv.sh
。这是为了防止人们在打算执行系统命令或环境变量中列出的某个目录中存在的其他文件时意外执行当前目录中的文件PATH
。
由于按名称运行文件(而不是使用source
或.
)会在新 shell 中运行它,因此它将拥有自己的一组 shell 变量。新 shell继承来自调用进程(在本例中是您的交互式 shell)的环境变量,并且这些环境变量确实会成为新 shell 中的 shell 变量。但是,要将 shell 变量传递给新 shell,必须满足以下情况之一:
shell 变量已导出,使其成为环境变量。
export
为此,请使用 shell 内置函数。在您的示例中,您可以使用export MY_VAR=12345
来一步设置和导出变量,或者如果已设置,则只需使用 即可export MY_VAR
。shell 变量是针对您正在运行的命令明确设置和传递的,因此在命令运行期间,它都是环境变量。这通常实现这一点:
MY_VAR=12345 ./testenv.sh
如果
MY_VAR
是尚未导出的 shell 变量,您甚至可以testenv.sh
通过MY_VAR
以下方式将其作为环境变量传递:将其设置为自身:MY_VAR="$MY_VAR" ./testenv.sh
./
脚本语法需要 Hashbang 行才能正确工作
顺便说一句,请注意,当你像上面那样通过名称调用可执行文件时(而不是使用.
或source
shell 内置命令),用于运行它的 shell 程序是不是通常由你运行它的 shell 决定。相反:
对于二进制文件,内核可能配置为运行该特定类型的文件。它检查文件的前两个字节,以查找“神奇数字”这表明它是哪种二进制可执行文件。这就是可执行二进制文件能够运行的方式。
这当然非常重要,因为脚本没有 shell 或其他解释器(可执行二进制文件)就无法运行!此外,许多命令和应用程序都是编译后的二进制文件,而不是脚本。
(
#!
是表示文本可执行文件的“神奇数字”的文本表示。)对于需要在 shell 或其他解释语言中运行的文件,第一行如下所示:
#!/bin/sh
/bin/sh
可以替换为用于运行该程序的其他任何 shell 或解释器。例如,Python 程序可能以以下行开头:#!/usr/bin/python
这些线被称为 hashbang、shebang 和许多其他类似的名称。请参阅此FOLDOC 条目, 这维基百科文章和#!/bin/sh 是否被解释器读取?了解更多信息。
如果文本文件标记为可执行并且你从你的 shell 运行它(例如
./filename
)但是它没有以 开头#!
,内核无法执行它。但是,看到这种情况发生,你的 shell 将会尝试通过将其名称传递给来运行它一些壳。有要求不多放在什么壳是(“该 shell 将执行一个相当于调用 shell 的命令...”)。在实践中,一些贝壳——包括bash
*--运行它们自己的另一个实例,而其他人使用/bin/sh
。我强烈建议您避免这种情况,而改用 hashbang 行(或通过将脚本传递给所需的解释器来运行脚本,例如bash filename
)。*GNU Bash 手册,3.7.2 命令搜索与执行:“如果由于文件不是可执行格式而导致执行失败,并且文件不是目录,则假定它是shell 脚本并且 shell 按照描述执行它Shell 脚本“”。
答案2
是的,你忽略了一些东西。
我认为你混淆了表示当前目录的“.”和./testenv.sh
表示source
(这是内置命令)。因此,在“.”表示的情况下,source
它将是. ./testenv.sh
。有意义吗?
尝试一下这个:
MY_VAR=12345
. ./testenv.sh