执行 Bash 脚本与获取该脚本有什么区别?

执行 Bash 脚本与获取该脚本有什么区别?

执行像 A 这样的 Bash 脚本和获取像 B 这样的 Bash 脚本之间有什么区别?

A
> ./myscript

B
> source myscript

答案1

获取和执行脚本都会逐行运行脚本中的命令,就像您逐行手动输入这些命令一样。

不同之处在于:

  • 当你执行您正在打开的脚本新的shell,在新 shell 中键入命令,将输出复制回当前 shell,然后关闭新 shell。对环境的任何更改都只会在新 shell 中生效,并且在关闭新 shell 后将丢失。
  • 当你来源您正在输入的脚本中的命令当前的shell。对环境的任何更改都将生效并保留在您当前的 shell 中。

“环境”包括诸如当前工作目录和环境变量之类的内容。还有 shell 设置(以及其他历史记录和完成功能)。还有更多,但这些是最明显的。

如果您希望脚本更改当前正在运行的 shell 中的环境,请使用 source。否则,请使用 execute。

如果您想了解更多详细信息,请继续阅读。

术语

澄清有关执行语法和源语法的一些常见混淆。

要执行的语法:

./myscript

这会执行 myscript前提是该文件是可执行文件并且位于当前目录中。前导点和斜杠 ( ./) 表示当前目录。这是必需的,因为当前目录通常不在(通常也不应该在)中$PATH

myscript

这会执行 myscript如果该文件是可执行文件并且位于某个目录中$PATH

语法来源:

source myscript

这会来源 myscript。该文件不需要可执行,但它必须是有效的 shell 脚本。该文件可以位于当前目录中,也可以位于 中的目录中$PATH

. myscript

这也将来源 myscript。此“拼写”是官方定义的POSIX.Bash 将其定义source为点的别名。

为了完整性:

exec myscript

这将终止当前 shell,然后执行 myscript 来代替终止的 shell。这意味着当 myscript 完成后,没有 shell 可以返回。exec功能强大但很少需要。

我在最后放了一些链接,以便提供有关这些主题的更多信息。

示范

考虑myscript.sh以下内容:

#!/bin/sh
# demonstrate setting a variable
echo "foo: "$(env | grep FOO)
export FOO=foo
echo "foo: "$(env | grep FOO)
# demonstrate changing of working directory
echo "PWD: "$PWD
cd somedir
echo "PWD: "$PWD

在执行脚本之前,我们首先检查当前环境:

$ env | grep FOO
$ echo $PWD
/home/lesmana

该变量FOO未定义,并且我们位于主目录中。

现在我们执行文件:

$ ./myscript.sh
foo:
foo: FOO=foo
PWD: /home/lesmana
PWD: /home/lesmana/somedir

再次检查环境:

$ env | grep FOO
$ echo $PWD
/home/lesmana

该变量FOO未设置且工作目录未改变。

脚本输出清楚地显示变量已设置且目录已更改。随后的检查显示变量未设置且目录未更改。发生了什么?更改是在新的壳。

内核衍生新的shell 运行脚本。新 shell 的输入和输出与当前 shell 相连。但除此之外,它是一个独立的 shell。脚本在新 shell 中运行,对环境的所有更改都会在新 shell 中生效。脚本完成后,新 shell 将被销毁。新 shell 中对环境的所有更改都会随新 shell 一起销毁。当前 shell 中只打印输出。

现在我们来源文件:

$ source myscript.sh
foo:
foo: FOO=foo
PWD: /home/lesmana
PWD: /home/lesmana/somedir

再次检查环境:

$ env | grep FOO
FOO=foo
$ echo $PWD
/home/lesmana/somedir

变量 FOO 已设置并且工作目录已改变。

获取脚本不会创建新的 shell。所有命令都在当前 shell 中运行,对环境的更改也会在当前 shell 中生效。

请注意,在这个简单的例子中,执行的输出与脚本的执行输出相同。但情况并不总是如此。

另一次示威

考虑以下脚本pid.sh

#!/bin/sh
echo $$

(特殊变量$$扩展为当前正在运行的 shell 进程的 PID)

首先打印当前shell的PID:

$ echo $$
25009

脚本来源:

$ source pid.sh
25009

执行脚本,记下PID:

$ ./pid.sh
25011

再次引用来源:

$ source pid.sh
25009

再次执行:

$ ./pid.sh
25013

您可以看到,脚本的执行在同一个进程中运行,而每次执行脚本都会创建一个新进程。这个新进程就是新的为执行脚本而创建的 shell。获取脚本不会创建新的 shell,因此 PID 保持不变。

概括

采购脚本将运行当前的shell 进程。对环境的更改在当前 shell 中生效。

执行脚本将运行新的shell 进程。对环境的更改将在新 shell 中生效,并在脚本执行完毕、新 shell 终止后丢失。

如果您希望脚本更改当前正在运行的 shell 中的环境,请使用 source。否则,请使用 execute。


也可以看看:

答案2

执行脚本时,它会在单独的子进程中运行,即调用单独的 shell 实例来处理脚本。这意味着脚本中定义的任何环境变量等不能在父(当前)shell 中更新。

获取脚本意味着当前 shell 本身会解析并执行该脚本。这就像您输入了脚本的内容一样。因此,获取的脚本不需要是可执行的。但是,如果您要执行它,它当然必须是可执行的。

如果当前 shell 中有位置参数,则它们不会改变。

因此如果我有一个a.sh包含以下内容的文件:

echo a $*

我也这么做:

$ set `date`
$ source ./a.sh

我得到类似这样的信息:

a Fri Dec 11 07:34:17 PST 2009

然而:

$ set `date`
$ ./a.sh

给我:

a

希望有所帮助。

答案3

采购本质上与在命令提示符下逐行输入脚本的每一行相同......

执行启动一个新进程,然后运行脚本的每一行,仅根据其返回的内容修改当前环境。

答案4

通过获取源,您可以获得脚本中定义的所有额外变量。
因此,如果您有配置或函数定义,则应该获取源而不是执行。执行独立于父环境。

相关内容