问题描述
我一直在寻找”.”和”source”内置命令之间的差异,以及一些来源(例如,在this讨论中以及bash手册中)表明它们是相同的。
但是,在环境变量出现问题之后,我进行了测试。我创建了一个文件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”命令,子环境从父环境继承所有变量,而对于”.”,子环境则没有。
我是否缺少某些东西,或者这是bash的未记录/不推荐使用的功能?
[GNU bash,版本4.1.5(1)-发行版(x86_64-pc-linux-gnu)]
最佳解决办法
简短答案
在您的问题中,第二个命令既不使用.
Shell内置也不使用source
内置。相反,实际上您是running the script在单独的 shell 中,可以像使用其他任何可执行文件一样通过名称来调用它。这确实为它提供了一组单独的变量(尽管如果将变量导出到其父 shell 中,则它是will be an environment variable for any child process,因此是will be included in a child shell’s variables)。如果将/
更改为一个空格,则可以使用.
内置来运行它,它等效于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变量,您甚至可以通过将MY_VAR
设置为自身来运行testenv.sh
,并将MY_VAR
作为环境变量传递:MY_VAR="$MY_VAR" ./testenv.sh
脚本的./
语法要求Hashbang行有效(正确)
顺便说一句,请注意,当您按上述名称调用可执行文件(而不是通过.
或source
shell 内置s调用)时,用于运行该程序的shell程序通常并不取决于您从哪个shell运行该程序。 。代替:
-
对于二进制文件,可以将内核配置为运行该特定类型的文件。它检查文件的前两个字节中是否存在“magic number”,该“magic number”指示其是哪种二进制可执行文件。这是可执行二进制文件能够运行的方式。当然,这是非常重要的,因为没有 shell 程序或其他解释程序(一个可执行的二进制文件)就无法运行脚本!另外,许多命令和应用程序都是编译的二进制文件,而不是脚本。 (
#!
是”magic number”的文本表示形式,指示文本可执行文件。) -
对于应该以Shell或其他解释语言运行的文件,第一行如下所示:
#!/bin/sh
/bin/sh
可以用任何其他要运行该程序的 shell 程序或解释器替换。例如,Python程序可能以以下行开头:#!/usr/bin/python
这些行称为hashbang,shebang和许多其他类似的名称。有关更多信息,请参见此FOLDOC entry,此Wikipedia article和解释器是否读取了#!/bin/sh?。
-
如果文本文件是marked executable,并且您从 shell 程序运行它(例如
./filename
),但是它不是以#!
开头,则内核将无法执行它。但是,看到发生了这种情况,您的shell将通过将其名称传递给某些shell来尝试运行它。在哪个 shell 上放置了few requirements(“ shell 应执行与调用 shell 一样的命令……”)。 In practice,其中一些 shell 程序(包括bash
*)运行其自身的另一个实例,而其他 shell 程序则使用/bin/sh
。我强烈建议您避免这种情况,而改用hashbang行(或通过将脚本传递给所需的解释器(例如bash filename
)来运行脚本)。 * GNU Bash manual,3.7.2 Command Search and Execution:“如果由于文件不是可执行文件格式而执行失败,并且该文件不是目录,则假定它是一个Shell脚本,并且Shell按照Shell Scripts的说明执行它。”
次佳解决办法
是的,您缺少了一些东西。
我认为您混淆了表示当前目录的’.’,如./testenv.sh
和表示source
的’.'(这是内置命令)一样。因此,当’.’表示source
时,它将是. ./testenv.sh
。说得通?
所以试试这个:
MY_VAR=12345
. ./testenv.sh
参考资料