1. Overview
In Linux, there are some simple commands. Usually, we know what the commands do. However, when we go a step further, sometimes, we can’t really tell the use cases of those commands.
The /bin/true and /bin/false commands are two examples.
In this tutorial, let’s take a closer look at these two pretty straightforward commands.
2. Introduction to /bin/true and /bin/false
Both /bin/true and /bin/false are members of the Coreutil package, which is available on almost all Linux distributions. It’s worth mentioning that some distributions may install the commands under /usr/bin instead of /bin.
If we look at the two commands’ man pages, the descriptions are pretty simple:
- /bin/true – “do nothing, successfully”
- /bin/false – “do nothing, unsuccessfully”
If we execute the commands, they really “do nothing”, except that the /bin/true command returns 0, while the /bin/false command returns 1:
$ /bin/false
$ echo $?
0
$ /bin/false
$ echo $?
1
Well, we’ve learned the two simple commands. But, we may ask, since they do nothing, why do we need the two commands? What are their use cases?
Next, let’s see some practical examples using the /bin/true and /bin/false commands.
3. Usages Example of /bin/false
First, let’s look at the /bin/false command.
We’ve learned that the /bin/false command does nothing but return a failure code. Its most typical usage is to deny shell access to a particular user account.
Next, let’s check which users’ shells on this machine are set to /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
When a user in the list above tries to log in via a shell, the /bin/false returns 1. In other words, it rejects the user’s shell access.
Now, let’s take the user nobody as an example to test:
root# su nobody
root# echo $?
1
Since we don’t know the user nobody‘s password, we log in as the root user and then try to change to the nobody user.
As the output above shows, after executing the “su nobody” command, the current user is still root. Also, the “su nobody” command failed with return code 1.
That is to say, the “switch user” operation failed.
Next, let’s see some example usages of /bin/true.
4. Using /bin/true to Build an Endless Loop
Since the /bin/true command always returns 0, we can use it to write an endless loop:
while /bin/true
do
some-operations
...
# We need to break out of the loop, otherwise the script will hang.
done
5. Making an Operation Always Successful
Sometimes, when some command’s execution gives a non-zero return code, the /bin/true command allows us to treat it as a success anyway.
Well, it sounds a bit strange. So next, let’s see an example. It may explain the scenario quickly.
5.1. The executor.sh Script
First, let’s see a simple shell script:
$ 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"
As the code above shows, the executor.sh script allows us to perform a predefined operation with an input. In this example, we’ve defined only one operation – findErrors.
The executor function is responsible for executing the given operation and doing some further common work afterward, such as logging execution status, error handling, and possibly many others.
It’s worth mentioning that, for simplicity, we’ve skipped argument validation in the script.
5.2. Executing findErrors on Two Input Files
Let’s say we have two log files:
$ 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 ..."
First, let’s perform the findErrors operation on the log2.log file:
$ ./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 ...
The output above shows that the executor has successfully performed the findErrors operation.
Next, let’s do the same with the other log file:
$ ./executor.sh findErrors log1.log
Start executing operation: [findErrors]
Failed! Logging and sending alert messages ...
This time, the executor reports that the operation failed, and the program enters error handling processes.
This is because the log1.log file doesn’t contain any “ERROR” entries. So, when grep finds no result, the grep command returns code 1 instead of 0.
Therefore, when the executor receives the return code 1, the operation is deemed to have failed.
Actually, this is not what we really want. Even though the findErrors operation finds no matches, the operation’s execution should be successful.
So, let’s see how to fix it.
5.3. Solving the Problem
Now, let’s use the /bin/true command to fix it:
#!/bin/bash
...
findErrors() {
grep -i 'error' "$INPUT"; /bin/true
}
...
We can concatenate the /bin/true command after the grep command with a semicolon. In this way, we ask the findErrors operation always to return 0, no matter if grep finds matches in the input or not.
Let’s test the findErrors operation with the log1.log file again:
$ ./executor.sh findErrors log1.log
Start executing operation: [findErrors]
Success! Logging status in the Execution-Database ...
As we can see, there is still no “ERROR“s found, but the operation’s execution succeeds.
Now, the program runs as expected.
6. Shell Built-ins and the Coreutils Package
6.1. The Shell Built-ins true and false
We’ve learned the /bin/true and the /bin/false commands through examples.
Sharp eyes may have found that we’ve been using /bin/true instead of true so far, although /bin is usually in the $PATH system variable.
This is because /bin/true and /bin/false from the Coreutils have brothers on Linux. Their brothers are the shell built-in true and false commands.
We can use the type -a command to list them, for example:
$ type -a true
true is a shell builtin
true is /bin/true
So, as we can see in the output above, we have two true commands on this system. One is the shell (Bash) built-in command, and the other is the /bin/true command from the Coreutils package.
In this part of the tutorial, we’ll focus on the true and false commands from the Coreutils package. Therefore, in previous sections, we use*/bin/true* or /bin/false.
If we simply execute true in a shell, we’ll execute the shell built-in true.
The built-in true and false have the same functionalities as their corresponding Coreutils commands.
6.2. The Bash Built-in true vs. /bin/true
We’ve learned that true and false do basically nothing. In other words, they should be very fast.
Next, let’s do an interesting test: benchmarking the Bash built-in true and the Coreutils true.
We’ll run each true command 10,000 times and use the time command to measure how long the executions take.
First, let’s measure the built-in Bash true:
$ time for i in {1..10000}; do true; done
real 0m0.015s
user 0m0.015s
sys 0m0.000s
It finished instantly! Next, let’s test the /bin/true command:
$ time for i in {1..10000}; do /bin/true; done
real 0m12.552s
user 0m10.863s
sys 0m10.013s
This time, we’ve waited about 13 seconds to see the output.
So, the built-in Bash true is much faster than the Coreutils version.
For example, on this machine, the built-in Bash true is about 836 (12.552/0.015) times faster than the Coreutils version. What a surprise!
6.3. Why Do We Need /bin/true and /bin/false From the Coreutils Package?
We’ve learned that the Bash built-in true and false and the Coreutils versions have the same functionality. Moreover, the built-in shell commands are much faster than their Coreutils version.
Then, we may ask, why do we still need the external commands /bin/true and /bin/false?
The answer is because built-in shell commands live only in a shell. Therefore, they’re only available if we call them from a shell.
In a previous example, we’ve shown using /bin/false in the /etc/passwd file to prevent a user from accessing a shell. It’s also a good example of calling /bin/false not from a shell.
Now, let’s change the user guest‘s shell to false and see what’ll happen when we switch to 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
The output above shows the su command failed as it cannot find the file or directory “false”. This is because it calls the false command, but not from a shell.
Therefore, all built-in shell commands are unavailable at this point.
7. Conclusion
In this article, we’ve learned two straightforward commands – /bin/true and /bin/false.
Further, we’ve discussed the difference between the shell built-ins true and false and the Coreutils versions.