看一下代码:
#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
read -p "Enter UID: " uid
logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
read -p "Enter Logname: " logname
fi
not=`ps -au$logname | grep -c bash`
echo "The number of terminals opened by $logname are $not"
此代码用于找出同一台 PC 上用户打开的终端数量。现在有两个用户登录,分别为 x 和 y。我目前以 y 身份登录,用户 x 中打开了 3 个终端。如果我使用上述不同方式在 y 中执行此代码,结果如下:
$ ./file.sh
The number of terminals opened by x are 3
$ bash file.sh
The number of terminals opened by x are 5
$ sh file.sh
The number of terminals opened by x are 3
$ source file.sh
The number of terminals opened by x are 4
$ . ./file.sh
The number of terminals opened by x are 4
注意:我将 1 和 uid 1000 传递给所有这些可执行文件。
现在你能解释一下这些之间的区别吗?
答案1
唯一的主要区别在于获取脚本和执行脚本。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
调用的脚本。不同之处在于您使用shell 明确运行它。在 Ubuntu 系统上,即而不是。通常,脚本有一个file.sh
sh
dash
bash
舍邦线给出了它们应该作为哪个程序运行。使用其他程序调用它们会覆盖该程序。例如:$ 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
该脚本将仅打印用于运行它的 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 不同的自己的 shell 中运行。举例来说:$ cat foo.sh #!/bin/bash foo="Script" echo "Foo (script) is $foo"
现在,如果我在父 shell 中将变量设置
foo
为其他值,然后运行脚本,则脚本将打印不同的值(因为它也在脚本中设置),但父 shell 中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
但是,如果我获取脚本而不是执行它,它将在同一个 shell 中运行,因此
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
因此,在少数情况下,当您希望脚本影响运行脚本的 shell 时,可以使用 sourcing。它通常用于定义 shell 变量,并在脚本完成后使它们可用。
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
命令`command`
。最后这一点很重要,使用命令替换(或)启动命令会导致启动父 shell 的副本并运行该命令。但是,由于显示其输出的$(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
。使用不同的 shell 直接启动 (
sh
)$ sh foo.sh The number of shells opened by terdon is 1
sh
这是不同的,因为你使用而不是 来运行脚本bash
。因此,唯一的bash
实例是你启动脚本的父 shell。上面提到的所有其他 shell 都由 运行sh
。采购(通过
.
或source
,相同的东西)$ . ./foo.sh The number of shells opened by terdon is 2
正如我上面所解释的,获取脚本会导致它在与父进程相同的 shell 中运行。但是,会启动一个单独的子 shell 来启动命令
ps
,这样总数就变成了两个。
最后要注意的是,计算正在运行的进程的正确方法不是解析,ps
而是使用pgrep
。如果你运行
pgrep -cu terdon bash
因此,始终打印正确数字的脚本的工作版本是(请注意没有命令替换):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
如果使用 source 则返回 1,如果使用其他所有方式启动则返回 2(因为将启动新的 bash 来运行脚本)。如果使用 启动,则仍将返回 1,sh
因为子进程不是bash
。