毕竟,bash中的“。”和“源”之间有区别吗?


37

我一直在寻找“。”之间的区别。和“源”内置命令以及一些源(例如,在讨论中以及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

[请注意第一种形式返回一个空字符串]

所以,这个小实验表明,有毕竟有差别,其中的“源”命令,孩子的环境继承所有从父之一,这里的变量“” 它不是。

我是否缺少某些东西,或者这是bash的未记录/不推荐使用的功能?

[GNU bash,版本4.1.5(1)-发行(x86_64-pc-linux-gnu)]

Answers:


67

简短答案

在您的问题中,第二个命令既不使用.shell内置程序,也不使用source内置程序。相反,实际上是通过在名称上调用脚本来像在其他任何可执行文件中一样在单独的外壳中运行脚本。这确实为它提供了一组单独的变量(尽管如果在其父外壳中导出变量,它将是任何子进程的环境变量,因此将包含在子外壳的变量中)。如果将更/改为空格,则可以使用.内置的空格来运行它source

扩展说明

这是source内置Shell 的语法,该语法在当前Shell中执行脚本的内容(并使用当前Shell的变量):

source testenv.sh

这是.内置语法,其功能与source

. testenv.sh

但是,此语法将脚本作为可执行文件运行,并启动新的shell来运行它:

./testenv.sh

那不是使用.内置的。相反,它.是您正在执行的文件的路径的一部分。一般来说,可以通过使用至少包含一个/字符的名称来调用外壳程序中的任何可执行文件。因此,在当前目录下运行文件./是最简单的方法。除非当前目录在您的目录中PATH,否则无法使用命令运行脚本testenv.sh。这是为了防止人们在打算执行系统命令或PATH环境变量中列出的某个目录中存在的其他文件时意外执行当前目录中的文件。

由于按名称运行文件(而不是使用source.),因此在新的外壳中运行该文件,因此它将具有自己的外壳变量集。新的shell 确实从调用过程中继承了环境变量(在本例中是您的交互式shell),这些环境变量的确成为了新shell中的shell变量。但是,要将shell变量传递给新的shell,必须满足以下条件之一:

  1. shell变量已被导出,使其成为环境变量。export为此使用内置的shell。在您的示例中,您可以使用export MY_VAR=12345一步来设置和导出变量,或者如果已经设置了变量,则可以简单地使用export MY_VAR

  2. shell变量是为您正在运行的命令显式设置和传递的,因此在运行命令期间,它是一个环境变量。通常,这可以实现以下目的:

    MY_VAR=12345 ./testenv.sh

    如果MY_VAR是尚未导出的shell变量,你甚至可以运行testenv.shMY_VAR作为环境变量传递通过它设置到其自身

    MY_VAR="$MY_VAR" ./testenv.sh

./ 脚本的语法需要哈希行才能正常工作

顺便说一句,请注意,当您按上述名称调用可执行文件(而不是使用.sourceshell内置程序)时,用于运行该程序的shell程序通常并不取决于您从哪个shell运行该程序。 。代替:

  • 对于二进制文件,可以将内核配置为运行该特定类型的文件。它检查文件的前两个字节是否有“幻数”,该“幻数”指示文件的二进制可执行文件类型。这是可执行二进制文件能够运行的方式。

    当然,这非常重要,因为没有外壳程序或其他解释程序(一个可执行的二进制文件)就无法运行脚本!另外,许多命令和应用程序都是编译的二进制文件,而不是脚本。

    #!是表示文本可执行文件的“魔术数字”的文本表示。)

  • 对于应该以Shell或其他解释语言运行的文件,第一行如下所示:

    #!/bin/sh

    /bin/sh可以用任何其他要运行该程序的外壳程序或解释程序替换。例如,Python程序可能以以下行开头:

    #!/usr/bin/python

    这些行称为hashbang,shebang和许多其他相似的名称。请参阅此FOLDOC条目,此Wikipedia文章以及解释器是否读取#!/ bin / sh?欲获得更多信息。

  • 如果一个文本文件被标记为可执行文件,并且您从shell中运行它(例如./filename),但它不是以开头#!,则内核将无法执行它。但是,看到发生了这种情况,您的shell将通过将其名称传递给某些 shell 来尝试运行它。有一些要求放在什么壳是(“壳应具有外壳执行命令调用等同......”)。在实践中,某些外壳程序(包括*)运行其自身的另一个实例,而另一些使用bash/bin/sh。我强烈建议您避免这种情况,而改用hashbang行(或通过将脚本传递给所需的解释器(例如bash filename)来运行脚本)。

    * GNU Bash手册3.7.2命令搜索和执行:“如果由于文件不是可执行文件格式并且该文件不是目录而导致执行失败,则假定它是一个Shell脚本,并且Shell按照说明执行它在Shell脚本中。”


2
我发现有用的source是,这些功能可以从bash中获得,而无需再次加载或启动。实例#!/bin/bash function olakease {echo olakease;}。加载后,source file.sh您可以直接olakease从bash 调用。我真的很喜欢 源代码执行,然后加载很多东西,点.仅用于执行,就像使用一样bash file.sh
erm3nda 2015年

2
@ erm3nda .也具有此行为:. file.shsource file.sh做完全相同的事情,包括保留中定义的功能file.sh。(也许您在想./file.sh,这是不同的。但这使用.内置函数;而是.路径的一部分。)
Eliah Kagan

哦!我没有仔细阅读。[space]文件。太谢谢你了。
erm3nda

13

是的,您缺少了一些东西。

我认为您混淆了“。” 表示当前目录,如./testenv.sh和。这意味着source(这是一个内置命令)。因此,当“。” 意味着source会的. ./testenv.sh。说得通?

所以试试这个:

MY_VAR=12345 
. ./testenv.sh

2
./确切告诉它该文件它,没有它,bash将期待通过PATH,然后再尝试将当前目录,如果没有找到它。如果bash在POSIX模式下运行,并且您没有提供文件的路径(如./),它将仅在PATH中搜索,并且如果当前目录不在PATH中,则找不到该文件。
geirha

@geirha是的,您是对的,source(和.)实际上将首先检查$ PATH,即使它们实际上并没有正常运行该脚本。我的(前)评论不正确。
伊利亚·卡根

简短并指向+1
David Morales
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.