1. Introduction

As the ubiquitous Linux shell, Bash has its own features and scripting syntax, which upgrade those of the regular sh shell. On the other hand, most shells agree on how the basic conditional expressions work at the logical and syntactic levels.

In this tutorial, we’ll explore how to construct ifelse statements on a single line. First, we look at Bash one-liners in general. Next, we do a brief refresher of conditional statements in terms of their structure and potential to stretch on multiple lines. After that, we get into compacting multi-line statements, focusing on if. Finally, we discuss a common pitfall with conditional statements.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. Bash One-Liners

While script files are the standard, sometimes we’re after an even simpler solution to a given task. In such cases, one-liners come in very handy. A one-liner is a single line of (Bash) code that executes an atomic task we may need to be done at a given moment.

There are many advantages of single lines over their script counterparts:

  • commonly adhere to the “one tool for one job” philosophy of UNIX
  • generally use coding idioms, which optimize speed and size
  • usually bare-bones, minimum red tape
  • portability in terms of copy-paste mechanics
  • easily convertible to an alias

Of course, there are disadvantages as well, especially because one-liners heavily depend on the skill of their author.

Still, there are web sites dedicated to snippets of line code dedicated to everyday tasks. For example, we can count open processes per user in one line:

$ ps hax -o user | sort | uniq -c
     10 root

Here, we can keep the code on a single line with only pipes between the commands. Alternatively, we’d have to use variables, storing the data between subshell calls:

$ line1=$(ps hax -o user)
$ line2=$(echo "$line1" | sort)
$ line3=$(echo "$line2" | uniq -c)
$ echo "$line3"
     10 root

Naturally, this is not optimal. On top of this, involving more advanced shell features such as loops and conditions, we might end up with some complex syntax.

3. Conditional Statements

As we’ve already seen, basic ifelse statements conform to the POSIX standard:

if COMMAND
then
  EXPRESSIONS
elif COMMAND
then
  EXPRESSIONS
else
  EXPRESSIONS
fi

This structure is far from a single line. Moreover, we can have many lines in EXPRESSIONS. On top of that, we can expand COMMAND with the () syntax, adding still more lines. Finally, there are the && and || operators, enabling multiple COMMAND statements in each condition:

if (
  (
    COMMAND1 &&
    COMMAND2
  ) ||
  COMMAND3
)
then
  EXPRESSIONS1
  EXPRESSIONS2
fi

Actually, we can even skip the ifelse structure, instead opting for the so-called ternary statement:

[ TEST_COMMAND ] && (
  THEN_EXPRESSIONS
) || (
  ELSE_EXPRESSIONS
)

Here, based on the result of TEST_COMMAND, the script either executes the THEN_EXPRESSIONS or ELSE_EXPRESSIONS.

Obviously, there’s a lot of spacing in the above scripts, much of which may not be needed. Let’s see how we can use that to our advantage.

4. Compacting Bash Conditional Structures

In most cases, the biggest space optimizations come from knowing where we actually need newlines and where they are just cosmetic improvements.

To begin with, we can omit all whitespace around the parentheses:

if ((COMMAND1 &&
  COMMAND2) ||
  COMMAND3)
then
  EXPRESSIONS1
  EXPRESSIONS2
fi

In fact, we can also leave only single spaces around the && and || operators:

if ((COMMAND1 && COMMAND2) || COMMAND3)
then
  EXPRESSIONS1
  EXPRESSIONS2
fi

Critically, the shell treats then as a statement, so we can only do with a single space after it:

if ((COMMAND1 && COMMAND2) || COMMAND3)
then EXPRESSIONS1
  EXPRESSIONS2
fi

The final but arguably most important step is replacing all other instances of a newline with a semicolon:

if ((COMMAND1 && COMMAND2) || COMMAND3);then EXPRESSIONS1;EXPRESSIONS2;fi

The rule for this step is that semicolons are equivalent to a newline within a list of commands. Furthermore, by doing this, we lower the number of lines in multi-line EXPRESSIONS.

5. The [ Builtin

Unlike parentheses (()), which are a syntax construct, brackets denote the start and end of the *[* builtin. In fact, *[* is essentially the test command.

Thus, similar to other commands, whitespace is required after opening and before closing brackets:

$ if [ $(echo TEST) ]; then echo CONDITION; fi
CONDITION

Let’s now try the same example without the proper spacing:

$ if [ $(echo TEST)]; then echo CONDITION; fi
bash: [: missing `]'
$ if [$(echo TEST) ]; then echo CONDITION; fi
bash: [TEST: command not found
$ if [$(echo TEST)]; then echo CONDITION; fi
bash: [TEST]: command not found

As the results from our attempts show, spacing is indeed critical in some cases.

6. Summary

In this article, we discussed single-line ifelse statements in contrast to their regular multi-line forms. By showing a few examples and transforming them into one-liners, we pointed out some pitfalls and considerations.

In conclusion, writing one-liners, in general, is an arguable practice unless done precisely and with attention to potential issues.