How to Use Bash Subshells Inside if Statements - JohnHau/mis GitHub Wiki
If you have ever used Bash subshells ($(...)), you know how flexible subshells can be. It only takes a few characters to start a subshell to process anything required, inline to another statement. The number of possible use cases is virtually unlimited.
We can also use Bash subshells inside if statements, inline with the statement. Doing so gives the user and developer much additional flexibility when it comes to writing Bash if statements.
If you are not familiar yet (or would like to learn more about) Bash if statements, please see our Bash If Statements: If Elif Else Then Fi article.
In this tutorial you will learn:
How to incorporate Bash subshells inside if statements Advanced methods to incorporate Bash subshells inline with other commands Examples demonstrating the use of Bash subshells in if statements
Example 2: A bit more complex if-based subshell statement $ VAR1='abc'; if "$(echo "${VAR1}")" == "b" ; then echo 'Matches!'; else echo 'Does not match!'; fi Matches! $ VAR1='adc'; if "$(echo "${VAR1}")" == "b" ; then echo 'Matches!'; else echo 'Does not match!'; fi Does not match! Here we set a variable VAR to either abc or adc and next output this variable, again using a subshell, against the presence of b in the string. Note that the original asterisk () prefix to the "b" compare clause indicates anything before this string and the suffix asterisk () similarly means anything after this string. We can see how b was found in the first abc string, but not in the second command/string where adc was used as a compare string.
Note also how we used ... brackets for the if statement this time. This is unrelated to the use of subshells, and it is simply a newer Bash standard of writing if statements which can be used for additional or other use cases then the traditional [...] syntax. We require it here to do the special b matching we are attempting, using the asterisk (*) prefix and suffix to the "b" compare clause.
In a if statement with single [...] brackets this would fail:
$ if [ "abc" == "b" ]; then echo 'Matches!'; else echo 'Does not match!'; fi Does not match! $ if "abc" == "b" ; then echo 'Matches!'; else echo 'Does not match!'; fi Matches! As the if [...] syntax does not recognize the asterisk (*) prefix and suffix to the "b" compare clause, and one needs to use ... brackets instead.
Another thing to note is that this time we used double quotes (") inside the subshell (instead of the single quotes like in the first example): when one starts a subshell, such use of double quotes is not only allowed, but I can highly recommend it for various uses cases. It is handy in some situations where a lot of complex parsing is going on and a mix of single and double quotes is necessary. The double quotes will not terminate the quotes started before and outside of the subshell.
Please note that with most of the previous examples, one could have simply left off the subshell and do a simple compare directly with for example the variable, i.e.:
$ VAR1='abc'; if "${VAR1}" == "b" ; then echo 'Matches!'; else echo 'Does not match!'; fi Matches! We chose however to introduce subshells with echo (effectively a null-operation, i.e. effectively the same as just using the variable or the text in question) as it would highlight that 1) subshells work effectively, and 2) that they can be used from within if statements.
Example 3: Advanced if-based subshell statements We do not need to restrict our subshell usage inside if statements to a single command, nor to the use of echo alone. Let’s make a small setup:
$ touch a $ ls --color=never ./a | wc -l 1
We created a file named a, and counted the number of lines (using wc -l, a counting tool which can count the number of lines by using the -l option). We also made sure to introduce the --color=never option to ls to avoid parsing issues when terminal color coding is used.
Next, let’s work these statements directly into if statements:
$ if [ -z "$(ls --color=never ./a | wc -l)" ]; then echo "Empty directory output!"; fi $ if [ "$(ls --color=never ./a | wc -l)" -eq 1 ]; then echo "Exactly one file found!"; fi Exactly one file found! $ Here we use the same ls ... wc -l code twice directly from within an if statement. The first if statement, which uses -z checks if the text between quotes (the first option to the -z if-instruction) is empty. It is not as the ls command will yield some output in this case, given that we created the file a.
In the second command, we actually test if the output from our ls ... wc -l command is equal to 1 by using the -eq test option in the if statement. eq stands for equal to. Note that -eq (and it’s reverse -ne being not equal to) can only be used for numbers. For text based strings, use == (equal) and != (not equal) instead.
The output of command (Exactly one file found!) is correct, and our if statement with incorporated multi-command subshell works fine!
Also of interest to note there is that the first compare value in the second if statement (i.e. $(ls --color=never ./a | wc -l) with output 1) is numeric. So, why have we used two double quotes ("...") around the subshell statement? This has nothing to do with subshells, and all with how if works in Bash, and one may not know this trick or shorthand yet; please consider this:
$ V='1 1' $ if [ ${V} -eq 0 ]; then echo '0'; fi bash: [: too many arguments $ if [ "${V}" -eq 0 ]; then echo '0'; fi bash: [: 1 1: integer expression expected $ V=0 $ if [ "${V}" -eq 0 ]; then echo '0'; fi 0 In other words, using double quotes is a slightly safer way of programming Bash if statements, even if the condition is a numeric based condition. It protects against more complex strings being interpreted as individual items rather then a single value, and it returns a correct error message (integer expression expected), instead of the more ambiguous bash: [: too many arguments error.
It also does not matter to Bash that you are comparing what seems to be a text string (as indicated by "...") with a numeric value; it works, provided that the number is numeric. And if it is not, it will still provide a better error message indicating that the string is not numeric, as seen. In summary, it is better to always quote your subshell, text or variable with double quotes, even when comparing numeric items. To prove this works well, consider:
$ if [ "1" -eq "1" ]; then echo 'y'; fi y $ if [ "1" -eq "0" ]; then echo 'y'; fi $ Conclusion In this article, we looked at incorporating Bash subshells inside if statements. We explored several examples, from easy to advanced, on how we can use Bash subshells inside if statements. We also dived a little into using double quotes when comparing, even when comparing numeric fields. Using subshells inside other commands, and in this case if statements is a powerful way to expand your Bash scripting skills. Enjoy!