Comparison and checking might not be suitable in real life, but it is essential in programming. It helps to ensure that you have good code that interacts well and works as expected. Comparison of various values, checking of the different properties of files, and logical reasoning with and/or methods is a massive part of any programming language and the entire basis of reasoning within any script’s framework. For Bash, the command test provides many of these features, and as we will see, an even more simplified version of that also exists. So let us know what it is all about.
Bash test command introduction
The manual page of the test quite states, “check file types and compare values,” which encloses a plethora of features. To give an overview, we use a test to compare numbers, if they are equal or greater/smaller. We use it to check whether two strings are similar or not and to see if a string is empty. We use it to check file types and permissions and to check for their existence. For such a versatile command, the test has a pretty straightforward syntax.
Return messages
One important thing to understand before learning the command is that the test command, by default, doesn’t have a return message. It ends with an exit code of 1 or 0, but we can’t see it if we do it on the command line. For example, if we enter a command to check if 1 is equal to 2:
test 1 -eq 2
Running this command as is doesn’t return a message. So we add a bit of code to return a message:
test 1 -eq 2 && echo "true" || echo "false"
If the comparison is true, this will return a string that says “true”. If not, it will return “false”.
This works because, in Bash, the “&&” token can be used to execute a message if the previous command is executed successfully and, where applicable, has a positive result. This means that if the result of our comparison is true, the first part next to the “&&” will be executed. On the other hand, the token “||” is executed when only when the first command is a failure. This was the case here, and this is the result that we saw.
Shortened format
This command has to frequently be used in bash that even a shortened form of this was created. To enter the same command as the case above, you can simply write:
[ 1 -eq 2 ] && echo "true" || echo "false"
It is essential to note the presence of a single space right after the opening square bracket and right before the closing one. The absence of those spaces results in a situation where Bash cannot recognize the syntax because the command becomes “[1”, which means nothing.
This doesn’t look like a drastic change in just one line, but in larger scripts, this makes a lot of difference for the performance and readability.
There are three significant categories of test commands:
Integer tests
Integer tests are the ones that are used to compare different integers, like which one is higher/lower or if they are equal. There are various combinations of these comparisons, which can be tested in a straightforward form. Given that int1 and int2 are the two integers that need to be compared, the expressions look like this:
Greater than
test int1 -gt int2 && echo "true" || echo "false"
Or
[ int1 -gt int2 ] && echo "true" || echo "false"
If int1 has a higher value than int2, the command with return “true”. If not, it will return “false”.
Less than
test int1 -lt int2 && echo "true" || echo "false"
Or
[ int1 -lt int2 ] && echo "true" || echo "false"
If int1 has a lower value than int2, the command with return “true”. If not, it will return “false”.
Equal to
test int1 -eq int2 && echo "true" || echo "false"
Or
[ int1 -eq int2 ] && echo "true" || echo "false"
If int1 and int2 have the same value, the command with return “true”. If not, it will return “false”.
Not equal to
test int1 -ne int2 && echo "true" || echo "false"
Or
[ int1 -ne int2 ] && echo "true" || echo "false"
If int1 and int2 don’t have the same value, the command with return “true”. If not, it will return “false”.
Greater than or equal to
test int1 -ge int2 && echo "true" || echo "false"
Or
[ int1 -ge int2 ] && echo "true" || echo "false"
If int1 has a value higher than int2 or is equal to int2, the command with return “true”. If not, it will return “false”.
Less than or equal to
test int1 -le int2 && echo "true" || echo "false"
Or
[int1 -le int2] && echo "true" || echo "false"
If int1 has a value lower than int2 or is equal to int2, the command with return “true”. If not, it will return “false”.
String tests
Strings are any set of characters put in a sequence. They might even all be integral characters, but defined as a string. You can define any set of a random set of characters as a string, as long as it doesn’t mess with Bash’s syntax rules. There are often cases where we need to compare strings or check for their validity. Assuming the strings as str1 and str2 (in case of a comparison), the tests look like this:
Non-zero string
test -n "str1" && echo "true" || echo "false"
Or
[ -n "str1" ] && echo "true" || echo "false"
If the string is not empty, meaning it has anything inside the double quotations, it will return “true”. Otherwise, it will return “false”.
Zero string
test -z "str1" && echo "true" || echo "false"
Or
[ -z "str1" ] && echo "true" || echo "false"
If the string is empty, meaning it has nothing inside the double quotations, it will return “true”. Otherwise, it will return “false”.
Equal strings
test "str1" = "str2" && echo "true" || echo "false"
Or
[ "str1" = "str2" ] && echo "true" || echo "false"
If both str1 and str2 are precisely the same, only then will the result will be “true”. Even a difference in an uppercase alphabet qualifies for inequality. Otherwise, the result will be “false”.
Unequal strings
test "str1" != "str2" && echo "true" || echo "false"
Or
[ "str1" != "str2" ] && echo "true" || echo "false"
If both str1 and str2 are not precisely the same, only then will the result will be “true”. Otherwise, the result will be “false”.
File tests
The cases of integers and strings are significant when taking in specific sections containing said integers or strings. But in the case of Bash, we will have to deal with files quite a lot. So if the file is file1 and file2 (in case of comparisons), the commands look like this:
Linked files
Inode number can be considered an identification number associated with each file on a Linux system. It is the property that makes every file unique. Now, if you want to check if two files have the same Inode numbers, that is, they are the same file, you can use the following command:
test file1 -ef file2 && echo "true" || echo "false"
Or
[ file1 -ef file2 ]&& echo "true" || echo "false"
But now you might be thinking, how are two files the same? Even if you were to create copies of a file, it would be a completely different file in itself. Well, it doesn’t have to do with duplicated files as much as it has to do with files that are linked. Linux provides an option for soft-linking files to create a file that links to another file. So if file1 is symlinked (soft linked) to file2, then file2 is nothing on its own, just an empty shell that refers to file1 for the content. In that case, the comparison turns out to be “true”.
Newer file
test file1 -nt file2 && echo "true" || echo "false"
Or
[ file1 -nt file2 ] && echo "true" || echo "false"
This is simple enough. If file1 is newer than file2, the result is “true”; otherwise, it is “false”.
Older file
test file1 -ot file2 && echo "true" || echo "false"
Or
[ file1 -ot file2 ] && echo "true" || echo "false"
If file1 is older than file2, the result is “true”; otherwise, it is “false”.
Existence and nature of the file
test -e file1 && echo "true" || echo "false"
Or
[ -e file1 ] && echo "true" || echo "false"
Yes, you can indeed check if a file even exists or not. All the other file-related tests first check whether the file exists. Only if it does, the test proceeds.
test -s file1 && echo "true" || echo "false"
Or
[ -s file1 ] && echo "true" || echo "false"
For a slight variation, this checks whether a file exists or not and, if it does, if it is empty or not, that is, if it has a size greater than zero or not.
test -f file1 && echo "true" || echo "false"
Or
[ -f file1 ] && echo "true" || echo "false"
This checks whether or not the file exists, and if it does, it is a regular file. The other case would be that it is a directory, in which the answer becomes “false”.
test -d file1 && echo "true" || echo "false"
Or
[ -d file1 ] && echo "true" || echo "false"
This check if the file exists and if it is a directory. If so, “true” will be returned. If not, “false”.
test -h file1 && echo "true" || echo "false"
Or
[ -h file1 ] && echo "true" || echo "false"
This one checks if the file is a symbolic link, the concept of which we just explained. If so, “true” will be returned. If not, “false”.
File permissions
There are three standard file permissions, which can all be tested through the test command: read, write and execute.
test -r file1 && echo "true" || echo "false"
Or
[ -r file1 ] && echo "true" || echo "false"
Checks if the file exists and can be read by the user.
test -w file1 && echo "true" || echo "false"
Or
[ -w file1 ] && echo "true" || echo "false"
Checks if the file exists and can be written/edited by the user.
test -x file1 && echo "true" || echo "false"
Or
[ -x file1 ] && echo "true" || echo "false"
Checks if the file exists and can be executed by the user.
There are many more variations of this command, including checking for block-special files, character-special files, sockets, etc. This can be checked using the man command:
man help
Conclusion
The help command, as we have just seen, is essential to ensure certain critical factors for creating specific programs. It provides and confirms things on a scale required by almost everything one may need. We hope that this article was helpful. Cheers!