唯一的主要区别是在源代码和执行脚本之间。source foo.sh
将提供它,并且您显示的所有其他示例也正在执行。更详细地:
./file.sh
这将执行一个file.sh
位于当前目录(./
)中的脚本。通常,运行时command
,shell将在您的目录中$PATH
查找名为的可执行文件command
。如果提供完整路径,例如/usr/bin/command
或./command
,则将$PATH
忽略并执行该特定文件。
../file.sh
基本上,./file.sh
除了file.sh
查找父目录(../
)而不是在当前目录中查找之外,其他基本相同。
sh file.sh
与等效sh ./file.sh
,如上所述,它将运行file.sh
当前目录中调用的脚本。区别在于您是在sh
外壳上显式运行它。在Ubuntu系统上,dash
不是bash
。通常,脚本有一个shebang行,该行给出应以其运行的程序。用不同的名称调用它们会覆盖该值。例如:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
该脚本将仅打印用于运行它的外壳的名称。让我们看看以不同方式调用时返回的内容:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
因此,使用调用脚本shell script
将覆盖shebang行(如果存在),并使用您告诉它的任何shell运行脚本。
source file.sh
要么 . file.sh
令人惊讶的是,这被称为采购脚本。关键字source
是shell内置.
命令的别名。这是在当前shell中执行脚本的一种方式。通常,执行脚本时,脚本将在与当前脚本不同的自己的Shell中运行。为了显示:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
现在,如果我foo
在父外壳程序中将变量设置为其他变量,然后运行脚本,该脚本将打印不同的值foo
(因为它也在脚本中设置),但foo
父外壳程序中的值将保持不变:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
但是,如果我提供脚本而不是执行脚本,它将在同一外壳中运行,因此foo
父级中的值将被更改:
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
因此,在少数情况下,如果您希望脚本影响运行脚本的外壳,就会使用源代码。它通常用于定义外壳变量,并在脚本完成后使它们可用。
考虑到所有这些,您得到不同答案的原因首先是您的脚本没有执行您认为的操作。它计算bash
出现在输出中的次数ps
。这不是开放终端的数量,而是正在运行的shell的数量(实际上,甚至还没有,但这是另一次讨论)。为了澄清起见,我对此做了一些简化:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
只需打开一个终端即可以各种方式运行它:
直接启动./foo.sh
。
$ ./foo.sh
The number of shells opened by terdon is 1
在这里,您正在使用shebang行。这意味着脚本可以通过此处设置的任何内容直接执行。这会影响脚本在输出中显示的方式ps
。而不是作为被列bash foo.sh
,它只会显示为foo.sh
,这意味着你grep
会错过它。实际上有3个bash实例正在运行:父进程,运行脚本的bash 和运行ps
命令的另一个 bash实例。最后一点很重要,使用命令替换(`command`
或$(command)
)启动命令会导致启动父外壳程序副本并运行该命令。但是,由于ps
显示输出的方式,此处未显示任何内容。
使用显式(bash)shell直接启动
$ bash foo.sh
The number of shells opened by terdon is 3
在这里,由于您使用进行运行bash foo.sh
,因此ps
将显示bash foo.sh
并对其进行计数。因此,这里显示了父进程,bash
正在运行的脚本和已克隆的shell(正在运行ps
),因为现在ps
都将显示它们中的每一个,因为您的命令将包含单词bash
。
使用其他外壳直接启动(sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
这是不同的,因为您使用sh
和不是运行脚本bash
。因此,唯一的bash
实例是启动脚本的父外壳程序。上面提到的所有其他Shell都由其运行sh
。
采购(通过.
或source
,同一件事)
$ . ./foo.sh
The number of shells opened by terdon is 2
如上文所述,获取脚本会导致脚本在与父进程相同的shell中运行。但是,将启动一个单独的子外壳来启动ps
命令,从而使总数增加到两个。
最后一点,计算正在运行的进程的正确方法不是解析ps
而是使用pgrep
。如果您只是跑步,所有这些问题都可以避免
pgrep -cu terdon bash
因此,始终显示正确数字的脚本工作版本为(请注意,没有命令替换):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
在所有其他启动方式中,返回时将返回1,而将返回2(因为将启动新的bash以运行脚本)。使用sh
子进程启动时,它在启动时仍将返回1 bash
。