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

ysa*_*sap 42 bash scripts environment-variables environment

我正在寻找“。”之间的区别。和“源”内置命令和一些源(例如,在讨论中,以及bash 手册页)表明它们是相同的。

但是,由于环境变量的问题,我进行了测试。我创建了一个testenv.sh包含以下内容的文件:

#!/bin/bash
echo $MY_VAR
Run Code Online (Sandbox Code Playgroud)

在命令提示符下,我执行了以下操作:

> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh

> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345
Run Code Online (Sandbox Code Playgroud)

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

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

我是否遗漏了什么,或者这是bash的未记录/不推荐使用的功能?

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

Eli*_*gan 73

简答

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

扩展说明

这是sourceshell 内置的语法,它在当前 shell 中执行脚本的内容(因此使用当前 shell 的变量):

source testenv.sh
Run Code Online (Sandbox Code Playgroud)

这是.内置的语法,它做同样的事情source

. testenv.sh
Run Code Online (Sandbox Code Playgroud)

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

./testenv.sh
Run Code Online (Sandbox Code Playgroud)

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

由于按名称运行文件(而不是使用source.)在新 shell 中运行它,因此它将拥有自己的一组 shell 变量。新 shell确实从调用进程(在本例中为交互式 shell)继承了环境变量,并且这些环境变量确实成为新 shell 中的 shell 变量。但是,要将 shell 变量传递给新 shell,必须是以下情况之一:

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

  2. shell 变量是为您正在运行的命令显式设置和传递的,使其在命令运行期间成为环境变量。这通常可以实现:

    MY_VAR=12345 ./testenv.sh
    
    Run Code Online (Sandbox Code Playgroud)

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

    MY_VAR="$MY_VAR" ./testenv.sh
    
    Run Code Online (Sandbox Code Playgroud)

./ 脚本的语法需要 Hashbang 行才能工作(正确)

顺便说一句,请注意,当您按上述名称调用可执行文件(而不是使用.sourceshell 内置程序)时,用于运行它的 shell 程序通常不是由您运行它的 shell 决定的. 反而:

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

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

    #!是指示文本可执行文件的“幻数”的文本表示。)

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

    #!/bin/sh
    
    Run Code Online (Sandbox Code Playgroud)

    /bin/sh可以替换为旨在运行该程序的任何其他 shell 或解释器。例如,Python 程序可能以以下行开头:

    #!/usr/bin/python
    
    Run Code Online (Sandbox Code Playgroud)

    这些行被称为 hashbang、shebang 和许多其他类似的名称。请参阅此FOLDOC 条目、此Wikipedia 文章以及解释器是否读取了 #!/bin/sh?想要查询更多的信息。

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

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

  • 我发现在 `source` 上有用的是,这些功能可以从 bash 中使用,而无需再次加载或启动。示例 `#!/bin/bash 函数 olakease {echo olakease;}`。一旦你用 `source file.sh` 加载它,你就可以直接从 bash 调用 `olakease`。我真的很喜欢那个。源执行然后加载很多东西,点`.` 仅用于执行,类似于使用`bash file.sh` (2认同)
  • @erm3nda `.` 也有这种行为:`. file.sh` 和 `source file.sh` 做的事情完全一样,包括保留 `file.sh` 中定义的函数。(也许你正在考虑`./file.sh`,它是不同的。但这*不*使用`.`内置;相反,`.`是路径的一部分。) (2认同)

use*_*477 15

是的,你错过了一些东西。

我认为你混淆了“。” 这意味着当前目录,如 in./testenv.sh和 '.' 这意味着source(这是一个内置命令)。所以在'.'的情况下 意味着source它会是. ./testenv.sh。有道理?

所以试试这个:

MY_VAR=12345 
. ./testenv.sh
Run Code Online (Sandbox Code Playgroud)

  • `./` 告诉它文件的确切位置,如果没有它,bash 将首先查看 PATH,如果没有找到,则尝试当前目录。如果 bash 在 POSIX 模式下运行,并且您没有提供文件的路径(如`./`),它只会在 PATH 中搜索,如果当前目录不在 PATH 中,则无法找到该文件。 (2认同)