Linux中的/bin/true和/bin/false命令
1. 概述
在 Linux 中,有一些简单的命令。通常,我们知道命令的作用。但是,当我们更进一步时,有时我们无法真正说出这些命令的用例。
/bin/true 和/bin/false 命令是两个示例。
在本教程中,让我们仔细看看这两个非常简单的命令。
2. /bin/true和*/bin/false*介绍
/bin/true和*/bin/false都是Coreutil 包的成员,它在几乎所有 Linux 发行版上都可用。值得一提的是,*某些发行版可能会在/usr/bin而不是*/bin*下安装命令**。
如果我们查看这两个命令的手册页,描述非常简单:
- /bin/true - “什么都不做,成功”
- /bin/false – “什么都不做,不成功”
如果我们执行这些命令,它们实际上“什么都不做”,除了*/bin/true命令返回 0,而*/bin/false命令返回 1:
$ /bin/false
$ echo $?
0
$ /bin/false
$ echo $?
1
好吧,我们已经学会了这两个简单的命令。但是,我们可能会问,既然它们什么都不做,为什么我们需要这两个命令呢?他们的用例是什么?
接下来,让我们看一些使用*/bin/true*和 /bin/false命令的实际示例。
3. /bin/false的用法示例
首先,让我们看一下*/bin/false*命令。
我们已经了解到*/bin/false*命令除了返回一个失败代码之外什么都不做。它最典型的用法是拒绝对特定用户帐户的 shell 访问 。
接下来,让我们检查一下这台机器上哪些用户的 shell 设置为*/bin/false*:
$ grep 'false' /etc/passwd
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/srv/ftp:/bin/false
http:x:33:33:http:/srv/http:/bin/false
nobody:x:99:99:nobody:/:/bin/false
rtkit:x:133:133:RealtimeKit:/proc:/bin/false
ntp:x:87:87:Network Time Protocol:/var/lib/ntp:/bin/false
当上面列表中的用户尝试通过 shell 登录时,/bin/false返回 1。换句话说,它拒绝用户的 shell 访问。
现在,我们以用户nobody 为例进行测试:
root# su nobody
root# echo $?
1
由于我们不知道用户nobody的密码,所以我们以root用户身份登录,然后尝试更改为nobody 用户。
如上面的输出所示,执行“ *su nobody”*命令后,当前用户仍然是 root。此外,“ *su nobody”*命令失败,返回码为 1。
也就是说,“切换用户”操作失败。
接下来,让我们看看*/bin/true*的一些示例用法。
4. 使用*/bin/true*构建无限循环
由于*/bin/true*命令总是返回 0,我们可以用它来写一个无限循环:
while /bin/true
do
some-operations
...
# We need to break out of the loop, otherwise the script will hang.
done
5. 使操作始终成功
有时,当某些命令的执行给出非零返回码时,/bin/true命令允许我们将其视为成功。
嗯,听起来有点奇怪。那么接下来,让我们看一个例子。它可以快速解释这种情况。
5.1. executor.sh脚本
首先,让我们看一个简单的 shell 脚本:
$ cat executor.sh
#!/bin/bash
#Arguments
OPERATION="$1"
INPUT="$2"
execute() {
echo "Start executing operation: [$1]"
$1 && echo "Success! Logging status in the Execution-Database ..." \
|| echo "Failed! Logging and sending alert messages ..."
}
# predefined operations
findErrors() {
grep -i 'error' "$INPUT"
}
# other opertions...
execute "$OPERATION"
如上面的代码所示,executor.sh脚本允许我们使用输入执行预定义的操作。在这个例子中,我们只定义了一个操作—— findErrors。
executor函数负责执行给定的操作并在之后做一些进一步的常见工作,例如记录执行状态、错误处理以及可能的许多其他工作。
值得一提的是,为简单起见,我们在脚本中跳过了参数验证。
5.2. 在两个输入文件上执行findErrors
假设我们有两个日志文件:
$ head *.log
==> log1.log <==
2021-11-22 11:23:22 [INFO] application starts successfully
2021-11-22 11:24:22 [INFO] clean-up job starts..
2021-11-22 11:24:52 [INFO] clean-up job done!
==> log2.log <==
2021-11-28 18:23:00 [ERROR] IO: File "foo/bar" not found!
2021-11-28 18:24:00 [INFO] User has changed profile.
2021-11-28 18:25:00 [ERROR] DB: Cannot exec SQL Statement "insert into ..."
首先,我们 对log2.log文件执行findErrors操作 :
$ ./executor.sh findErrors log2.log
Start executing operation: [findErrors]
2021-11-28 18:23:00 [ERROR] IO: File "foo/bar" not found!
2021-11-28 18:25:00 [ERROR] DB: Cannot exec SQL Statement "insert into ..."
Success! Logging status in the Execution-Database ...
上面的输出表明执行器已经成功执行了findErrors操作。 接下来,让我们对另一个日志文件做同样的事情:
$ ./executor.sh findErrors log1.log
Start executing operation: [findErrors]
Failed! Logging and sending alert messages ...
这次执行器报告操作失败,程序进入错误处理流程。
这是因为log1.log文件不包含任何“ ERROR ”条目。因此,当 grep没有找到结果时, grep命令返回代码 1 而不是 0。
因此,当执行器收到返回码1时,就认为操作失败了。
实际上,这并不是我们真正想要的。即使findErrors操作找不到匹配项,该操作的执行也应该是成功的。 那么,让我们看看如何修复它。
5.3. 解决问题
现在,让我们使用*/bin/true*命令来修复它:
#!/bin/bash
...
findErrors() {
grep -i 'error' "$INPUT"; /bin/true
}
...
我们可以 在grep命令之后用分号连接 /bin/true命令。这样,无论grep是否在输入中找到匹配项,我们都要求findErrors操作始终返回 0 。 让我们再次使用log1.log文件测试findErrors操作:
$ ./executor.sh findErrors log1.log
Start executing operation: [findErrors]
Success! Logging status in the Execution-Database ...
可以看到,仍然没有找到“ ERROR ”,但是操作执行成功了。
现在,程序按预期运行。
6. Shell 内置程序和 Coreutils 包
6.1. Shell内置真假
我们通过示例学习了*/bin/true和/bin/false命令。*
敏锐的眼睛可能已经发现到目前为止我们一直在使用*/bin/true而不是true*,尽管*/bin*通常位于 $PATH 系统变量中。
这是因为Coreutils 中的*/bin/true和/bin/false在 Linux 上有兄弟。他们的兄弟是shell内置的真假*命令。
我们可以使用 type -a命令来列出它们,例如:
$ type -a true
true is a shell builtin
true is /bin/true
所以,正如我们在上面的输出中看到的,我们在这个系统上有两个真正的命令。一个是 shell (Bash) 内置命令,另一个是 Coreutils 包中的*/bin/true*命令。
在本教程的这一部分,我们将重点关注Coreutils包中的真假命令。因此,在前面的部分中,我们使用*/bin/true或/bin/false*。
如果我们只是在 shell 中执行true,我们将执行 shell 内置的true。
内置的true和false具有与其对应的 Coreutils 命令相同的功能。
6.2. Bash 内置true与*/bin/true*
我们已经了解到,*真假 *基本上什么都不做。换句话说,它们应该非常快。
接下来,让我们做一个有趣的测试:对 Bash 内置true和 Coreutils true进行基准测试。
我们将运行每个真正的命令 10,000 次,并使用time 命令来测量执行所需的时间。
首先,让我们测量内置的 Bash true:
$ time for i in {1..10000}; do true; done
real 0m0.015s
user 0m0.015s
sys 0m0.000s
一下子就结束了!接下来,让我们测试一下*/bin/true*命令:
$ time for i in {1..10000}; do /bin/true; done
real 0m12.552s
user 0m10.863s
sys 0m10.013s
这一次,我们等待了大约 13 秒才能看到输出。
所以,内置的 Bash true比 Coreutils 版本快得多。
例如,在这台机器上,内置的 Bash true比 Coreutils 版本快大约 836 (12.552/0.015) 倍。多么惊喜!
6.3. 为什么我们需要Coreutils 包中的*/bin/true和/bin/false* ?
我们了解到 Bash 内置true和 false和 Coreutils 版本具有相同的功能。此外,内置的 shell 命令比它们的 Coreutils 版本快得多。
那么,我们可能会问,为什么我们还需要外部命令*/bin/true和/bin/false*?
答案是因为内置的 shell 命令只存在于 shell 中。因此,只有当我们从 shell 调用它们时它们才可用。
在前面的示例中,我们展示了在*/etc/passwd文件中使用/bin/false来防止用户访问 shell。这也是不从 shell调用/bin/false*的一个很好的例子。
现在,让我们将用户guest的 shell 更改为 false ,看看当我们切换到guest时会发生什么:
$ grep '^guest' /etc/passwd
guest:x:1001:1001::/home/guest:false
$ su guest
Password:
su: failed to execute false: No such file or directory
上面的输出显示 su命令失败,因为它找不到文件或目录“false”。这是因为它调用了false命令,而不是从 shell 调用。
因此,此时所有内置的 shell 命令都不可用。