Ram*_*ddy 14 command-line bash scripts
看看代码:
#!/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"
Run Code Online (Sandbox Code Playgroud)
此代码用于查找用户在同一台 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
Run Code Online (Sandbox Code Playgroud)
注意:我将 1 和 uid 1000 传递给所有这些可执行文件。
现在你能解释一下所有这些之间的区别吗?
ter*_*don 24
唯一的主要区别在于获取脚本和执行脚本之间的区别。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在当前目录中调用的脚本。不同之处在于您使用shshell显式运行它。在 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
Run Code Online (Sandbox Code Playgroud)
该脚本将简单地打印用于运行它的 shell 的名称。让我们看看它在以不同方式调用时返回什么:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Run Code Online (Sandbox Code Playgroud)
因此,调用脚本 withshell 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"
Run Code Online (Sandbox Code Playgroud)
现在,如果我foo在父 shell中将变量设置为其他值,然后运行脚本,脚本将打印不同的值foo(因为它也在脚本中设置)但foo父 shell 中的值将保持不变:
$ 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
Run Code Online (Sandbox Code Playgroud)
但是,如果我获取脚本而不是执行它,它将在同一个 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
Run Code Online (Sandbox Code Playgroud)
因此,在您希望脚本影响正在运行它的 shell 的少数情况下,会使用采购。它通常用于定义 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"
Run Code Online (Sandbox Code Playgroud)
并以各种方式运行它,只打开一个终端:
直接启动,./foo.sh。
$ ./foo.sh
The number of shells opened by terdon is 1
Run Code Online (Sandbox Code Playgroud)
在这里,您使用的是shebang 行。这意味着脚本由那里设置的任何内容直接执行。这会影响脚本在ps. bash foo.sh它不会被列为,而只会显示为foo.sh,这意味着您grep会错过它。实际上有 3 个 bash 实例在运行:父进程、运行脚本的 bash和运行ps命令的另一个bash 实例。最后一点很重要,使用命令替换 (`command`或$(command))启动命令会导致启动并运行该命令的父 shell 的副本。但是,由于ps显示其输出的方式,这里没有显示这些内容。
使用显式 (bash) shell 直接启动
$ bash foo.sh
The number of shells opened by terdon is 3
Run Code Online (Sandbox Code Playgroud)
在这里,因为您正在运行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
Run Code Online (Sandbox Code Playgroud)
这是不同的,因为您正在运行脚本,sh而不是bash。因此,唯一的bash实例是您启动脚本的父 shell。上面提到的所有其他 shell 都由它来运行sh。
采购(通过.或source,同样的事情)
$ . ./foo.sh
The number of shells opened by terdon is 2
Run Code Online (Sandbox Code Playgroud)
正如我上面所解释的,获取脚本会导致它在与父进程相同的 shell 中运行。但是,会启动一个单独的子 shell 来启动ps命令,从而使总数达到两个。
最后要注意的是,计算正在运行的进程的正确方法不是解析ps而是使用pgrep. 如果你只是跑步,所有这些问题都可以避免
pgrep -cu terdon bash
Run Code Online (Sandbox Code Playgroud)
因此,始终打印正确数字的脚本的工作版本是(注意没有命令替换):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Run Code Online (Sandbox Code Playgroud)
对于所有其他启动方式,这将在获取时返回 1(因为将启动一个新的 bash 来运行脚本)和 2。sh由于子进程不是 ,因此在启动时仍会返回 1 bash。
| 归档时间: |
|
| 查看次数: |
7942 次 |
| 最近记录: |