Linux Complex Bash One-Liner Examples

Bash one-liners can reduce workload, automate something quickly and put the power of ultimate system control in your hands. Over time, you will likely learn to write more complex one-liners and some of the things you end up writing as a seasoned professional will be nearly in-parsible by a beginner. That said, the Bash command and development language is highly structured – and relatively easy to understand – once you know about the in and outs. It really is like becoming proficient in a foreign language.

In this tutorial you will learn:

  • How to write more advanced Bash one-liner commands and scripts
  • Understand how to combine various commands into one-liner scripts
  • Understand how exit codes from one command can affect other commands when using && and ||
  • Understand how input from a command can be modified and then be used by the next command
  • Usage and real-life like examples of more advanced Bash one-liners

Linux Complex Bash One-Liner Examples

Linux Complex Bash One-Liner Examples

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Linux Distribution-independent
Software Bash command line, Linux based system
Other Any utility which is not included in the Bash shell by default can be installed using sudo apt-get install utility-name (or yum install for RedHat based systems)
Conventions # – requires linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires linux-commands to be executed as a regular non-privileged user

Example 1: Process control

Let us start with an example of how to terminate certain processes in Bash in an easy-to-follow fashion:

$ sleep 3600 &
[1] 1792341
$ ps -ef | grep 'sleep'
roel     1792441 1701839  0 12:59 pts/13   00:00:00 sleep 3600
roel     1792452 1701839  0 12:59 pts/13   00:00:00 grep --color=auto sleep


First we setup a sleep command, for 3600 seconds (one hour), and we subsequently find that process in the process list. Great, but we do have the actual grep command as an extra line in the process listing output. Let’s filter that and also extract the process ID next instead of the full process information output:

$ ps -ef | grep 'sleep' | grep -v grep
roel     1792441 1701839  0 12:59 pts/13   00:00:00 sleep 3600
$ ps -ef | grep 'sleep' | grep -v grep | awk '{print $2}'
1792441

In the first command, we filtered out the active grep. In the second command we took this one step further by printing the second column $2 (inside awk) by using the awk command. We can now use take that one step further and actually kill that process. Let’s say we do that with signal 9 which is highly destructive to any Linux process (SIGKILL):

$ ps -ef | grep 'sleep' | grep -v grep | awk '{print $2}' | xargs kill -9
[1]+  Killed                  sleep 3600

And we can see our process was killed correctly. Whereas this was a more simple example, it involved 6 different commands: ps, grep, grep again, awk, xargs and kill. You can see how Bash one-liners can quickly build complexity in many different ways and at many different levels of complexity and data processing ability.

And, to learn more about xargs, please see our articles xargs for beginners with examples and multi threaded xargs with examples.

Example 2: Fun with success and failure!

$ echo '0' > a && echo '1' > b && echo '2' > c && ls doesnotexist || ls a && ls b && ls c && ls d && ls e
ls: cannot access 'doesnotexist': No such file or directory
a
b
c
ls: cannot access 'd': No such file or directory


What a complex line! Yet once you know how to read it, or perhaps you already do, it becomes very easy to read. Let us demonstrate this assertion to be valid by breaking up the command in smaller bite-size chunks which are easier to understand and follow:

$ echo '0' > a && echo '1' > b && echo '2' > c

All this set of commands does is the same as the following with one small caveat:

$ echo '0' > a
$ echo '1' > b
$ echo '2' > c

So what is the difference (and the small caveat)?

That in this last series of commands each command will be executed, no matter what the outcome of the previous command was. The previous sequence (using &&) will only proceed to the second echo if the outcome of the first command was 0 (i.e. success – in Bash success in a command is indicated by 0 and failure with 1 or higher as an exit code).

Thus, the command sequence using && could also be written as follows;

$ echo '0' > a
$ if [ ${?} -eq 0 ]; then echo '1' > b; fi
$ if [ ${?} -eq 0 ]; then echo '2' > c; fi

The ${?} (or $? in short syntax) variable always contains the outcome of the last command, i.e. the exit code (0, 1 or higher) generated by the last command.

As we can see, the one-line creation of echo '0' > a && echo '1' > b && echo '2' > c surely is easier on the eyes and understanding now, and it definitely reduces the complexity of the corresponding and matching code displayed just above.

Let’s next take only one command more:

$ echo '0' > a && echo '1' > b && echo '2' > c && ls doesnotexist
ls: cannot access 'doesnotexist': No such file or directory

This now reads a lot easier, right?

We just added another command namely ls doesnotexist provided that the command prior to it (and in this case the whole line as all commands are joined by && in a chain-like setup, where a faulty command will break the chain and stop execution of the chain in full) has succeeded. As all commands succeed, the ls is executed, and an error is produced as a result of the same because the file, well, really does not exist 🙂

So what would happen if we joined another && at the end? Would the chain of commands terminate as we said? Let’s tweak the command a bit:

$ echo '0' > a && echo '1' > b && echo '2' > c && ls doesnotexist && echo 'surely not'
ls: cannot access 'doesnotexist': No such file or directory


And, surely it did not execute. Let’s then introduce our next command in our chain from the original example:

$ echo '0' > a && echo '1' > b && echo '2' > c && ls doesnotexist || ls a
ls: cannot access 'doesnotexist': No such file or directory
a

Can you see what is happening? Here we have a new syntax symbol namely || which is different from && in that it executes only if there was a non-zero outcome in the previous command. Note that both || and && apply to only the last command, and not the chain of commands, even though one could think about it as a chain overall.

You can thus think about && as the English language equivalent and and, to some extent the common and present in programming languages, but with the twist that here we are checking for a condition before the && and executing what is behind it provided the exit condition is 0.

Another twist is that most programming languages will check for true-ness as a binary 1 when && syntax is used. For example consider the pseudo code; if test1_flag && test2_flag then... which will usually evaluate to true overall (and thus execute the then commands) if the binary flags test1_flag and test2_flag are 1 or true, whereas in Bash true-ness is indicated by a 0 (and not 1) exit status from the last command!

You can think of || as the English language equivalent or (or as in or if this fails then do…). In this situation there is a stronger connection with common programming languages: when a common program language checks for, for example if test1_flag || test2_flag then ..., then a binary positive test1_flag (i.e. value 1) or test2_flag would yield the overall condition to be true (and thus the then clause would be executed). We see the same in Bash; if the exit code of the command is non-zero (i.e. 1 or a higher value in some cases), then the command behind the || clause will be executed.

Let us now go back to the original command and parse it in full:

$ echo '0' > a && echo '1' > b && echo '2' > c && ls doesnotexist || ls a && ls b && ls c && ls d && ls e
ls: cannot access 'doesnotexist': No such file or directory
a
b
c
ls: cannot access 'd': No such file or directory

Can you see what happens? Because the ls doesnotexist command fails internally and yields a non-zero output (use ls doesnotexist; echo $? in Bash to verify; the output is 2), the or (||) clause is triggered and next we execute ls. Picture it like a chain flowing towards a different direction, but it still is a chain.

As the ls a command succeeds, and is followed by the and (&&) clause, the next command is executed and so on. Note that execution gets to ls d, and the output for the same (ls: cannot access 'd': No such file or directory) is shown, but the ls e command is not executed! This is expected, as && was used and the ls d command failed. Hence, ls e is never executed.

Conclusion

The more proficient you become in writing Bash one-liners, the faster, better, less error-prone and smoother your Bash one-liner scripts will become, and the less time you will spent on writing them. The developers of the Bash language have put all control in your hands. What will you do with that control today?

Leave us a message below with your coolest one-liner creations!



Comments and Discussions
Linux Forum