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