1. Overview

Single-line command statements are the norm on Linux command lines. However, sometimes we may need, or simply find it efficient, to run multiple commands from the prompt.

In this tutorial, we’ll look at various methods for running multi-line commands from a single command-line prompt.

2. Multi-Line Code Techniques

There are several techniques we can use, such as:

  • Segmenting with a backslash at the end of the line
  • Enclosing with parentheses
  • Confining within curly braces
  • Employing the EOF tag
  • Using the double ampersand (&&) or semicolon (;) operator
  • And most reliably, writing a script

Let’s look at each one in detail.

2.1. Using a Backslash

The backslash (*\*) is an escape character that instructs the shell not to interpret the next character. If the next character is a newline, the shell will read the statement as not having reached its end. This allows a statement to span multiple lines.

This is a useful technique that divides lengthy commands into readable statements contained within the shell session’s window.

Backslashes also break down command statements into understandable parts. Hence, they can be useful in breaking down a complex command into its individual operations. The backslash even allows us to execute multiple commands on multiple lines from a single prompt.

Let’s look at an example of using backslashes to segment a lengthy complex command:

$ find /etc -exec grep\
>'[0-9][0-9]*[.][0-9][0-9] \
> *[.][0-9][0-9]*[0-9][0-9]*' {} \;

2.2. Using Parentheses

Parentheses create a subshell within the main shell session and run the list of commands within that subshell. Even though the subshell imports variables from the main shell session, it doesn’t export the new variables created within it but restricts their use to within its own scope.

These new variables cease to exist once the subshell has completed its execution. Consequently, even without a variable declaration, a subshell can run multiple commands from a single command prompt.

Let’s look at an example of a parenthesized subshell:

$ a=45
$ b=23
$ (
> c=`expr $a + $b`
> echo $c
> d=`expr $c + $a`
> echo $d
> )
68
113
$ echo $d
 
$ echo $c
 
$

We can see that the variables declared within the subshell are empty in the outer shell.

2.3. Using Curly Braces

Curly braces { } are commonly used in shell commands to build arrays or to achieve parameter expansion. However, we can also run a set of commands from a single prompt by enclosing them in curly braces.

Unlike parentheses, which start a subshell, curly braces run those commands within the current shell environment.

This means not only that the main shell session’s exported variables are accessible to the section in curly braces; the commands within the curly braces also export their variables to the main shell session:

$ a=60
$ b=45
$ {
> c=`expr $a + $b`
> echo $c
> d=`expr $c + $a`
> echo $d
> }
105
165
$ echo $c
105
$ echo $d
165

We can see that those variables declared within the curly braces are still active outside it.

2.4. Using the EOF tag

While EOF is the default text for this method, Here Tag or Here Document is the official name. This tag instructs the shell environment to read all the input until reaching the same delimiter or tag. The shell environment can be any of the known Linux shells — that is, bash, sh, csh, tcsh, zsh, or ksh. Hence, if word is the Here tag, the shell will read all input redirected to it until word appears again.

Note: We cannot assign values to variables in Here Tags, yet we can process imported variables.

Let’s look at an example:

$ a=50
$ b=60
$ sh << word
> echo "Equation: a + b = 110"
> echo $(($a + $b))
> echo ""
> echo "Inside Here Tag, Assignment c=110"
> c=`expr $a + $b`
> echo $c
> word
Equation: a + b = 110
110

Inside Here Tag, Assignment c=110

$ echo "Outside Here Tag, Assignment c=110"
$ echo $c

We see that the new variable c received no value despite processing the same arithmetic operation prior to it.

Historically, the default used text is EOF — an acronym for “End of File”, which suits such an activity. STOP is also widely used, though EOF is more popular and more descriptive.

We’re free to use any word, acronym, or syntax as the delimiter. However, sticking to either EOF or STOP is wiser. They are obvious to this method, and mistaking them for other commands is unlikely.

Let’s look at an example using an EOF (Here) tag, redirected to a Bash shell:

$ a=50
$ b=60
$ bash << EOF
> echo $(( $a + $b ))
> EOF
110

Let’s now look at the same example, but redirected to a Bourne shell:

$ a=50
$ b=60
$ sh << EOF
> echo $(( $a + $b ))
> EOF
110

We see that the syntax and results are the same.

2.5. Using Double Ampersand

The double ampersand (&&) is a boolean operator that allows a command to execute if and only if the previous command or statement executed without error. This operator is useful when working with sequential commands whose results determine the execution of the next one.

The && operator is often used to adjoin tasks after a cd has been issued. Hence, the && operator is further beneficial in decreasing typing and streamlining our logic:

$ echo "The beginning of good things to come" > ThingsHeard.txt &&
> cat ThingsHeard.txt &&
> echo "Greater still, things to be done" >> ThingsHeard.txt &&
> cat ThingsHeard.txt &&
> cd txt &&
> echo "txt directory is non-existent" &&
> echo "These last two lines won't appear"
The Beginning of good things to come
The Beginning of good things to come
Greater still, things to be done
bash: cd: txt: No such file or directory

We see the subsequent commands remained unprocessed after encountering the erroneous statement.

2.6. Using Semicolons

The semicolon (;) is similar to the && operator in that defines a list of sequential commands. However, the semicolon is a command separator rather than an adjoining operator. It allows a set of sequentially listed commands to execute regardless of the result of the previous ones. Therefore, the semicolon is useful when the commands are not dependent on each other:

$ cd doc ; cd txt ; echo "txt directory is non-existent" ; echo "sequential commands are not bothered" 
bash: cd: txt: No such file or directory
txt directory is non-existent
sequential commands are not bothered

Note that all commands were executed despite the erroneous statement.

2.7. Combining Techniques

Each of the methods we’ve discussed up to this point has its advantages and limitations. However, by combining some or all of these methods, we can overcome their limitations. For instance:

  • The (&&) and (;) can execute a multi-line code that runs commands that are dependent and then independent of previous statements
  • A subshell can include commands listed within curly braces or the EOF tag
  • Curly braces could include a subshell and/or EOF tag
  • The EOF tag can include subshells and curly braces
  • A subshell can contain another subshell within it
  • Curly braces could nest other curly braces
  • The EOF tag could include other EOF tags

Also, let’s note there are some limitations, too:

  • All other techniques can nest the backslash technique
  • However, the backslash technique cannot nest other techniques within it except the double ampersand (&&)
  • The Here tag (EOF) can only import variables from its most immediate shell session. Hence, if we’re nesting a Here tag inside curly braces, or a subshell, it will import variables from those sessions. It will not import from any other session prior, including the main shell session

Let’s look at an example of nested multi-line code:

$ a=20
$ b=15
$ {
> c=`expr $a + $b`
> echo $c
> echo -e "\nAn example of using the backslash \
> within curly braces as a nested multi-line code."
> 
> (
> d=`expr $c + $a`
> echo $d
> 
> sh << EOF
> 
> echo $(($c + $b))
> 
> cd txt &&
> echo -e "\n txt directory is non-existent, therefore, \
> this statement will not display."
> 
> EOF
> )
> 
> echo -e "\nSince the variable d was declared in a subshell \
> will it display outside the subshell? Let's see:"
> 
> echo $d
> 
> echo "No it didn't"
> 
> echo -e "\nWill the variable e print? \
> It was nested inside a Here tag, inside \
> a sub shell."
> 
> echo $e
> 
> bash << EOF
> 
> {
> cd doc &&
> echo "Greater still, the opportunities." >> ThingsHeard.txt &&
> cat ThingsHeard.txt
> (
> echo -e "\nA nested backslash statement \
> within a subshell, within curly braces."
> 
> cd txt ;
> 
> echo -e "\nEven though the directory txt is non-existent, \
> this statement will still print since the previous command \
> was separated with a semicolon (;)."
> 
> )
> }
> EOF
> }

Let’s see the output from these commands:

35

An example of using the backslash within curly braces as a nested multi-line code.
55
50
sh: 5: cd: can't cd to txt

Since the variable d was declared in a subshell will it display outside the subshell? Let's see:

No it didn't

Will the variable e print? It was nested inside a Here tag, inside a sub shell.


The Beginning of good things to come
Greater still, the opportunities.

A nested backslash statement within a subshell, within curly braces.
bash: line 11: cd: txt: No such file or directory

Even though the directory txt is non-existent, this statement will still print since the previous command was separated with a semicolon (;).

2.8. Writing a Shell Script

Finally, the best technique for running multi-line shell code is scripting. Using shell scripts, we can edit and adjust our commands without having to rewrite them. Also, they allow us to use the “write-once, run-many” principle for repetitive tasks. Additionally, scripts are the best option when we’re dealing with commands whose syntax or logic is complex.

Of course, we can use the previously mentioned techniques in a script. Let’s look at an example of a script containing multi-line code:

#!/bin/bash

#########################################
# This is a script that uses multi-line #
# code methods within it        #
#########################################

a=$RANDOM
b=$RANDOM

{

c=`expr $a + $b`
echo $c

echo -e "\nA backslash multi-line code example \
within a script.\n"

(
d=`expr $c + $a`
echo $d

echo

cd doc &&

cat ThingsHeard.txt

echo 

cd txt ;

echo -e "\n txt directory is non-existent \
yet the next command will still run because \
the semicolon is used as a separator.\n"

cat ThingsHeard.txt

)

bash << EOF

echo -e "\nA Here tag multi-line code example \
nested within curly braces, in a script."

EOF

}

Let’s look at an example of the above script called within curly braces:

${
> a=8
> b=9
> t=`expr $a + $b`
> echo $t
> echo
> ./multiline.sh 
> }

Now, let’s see the resulting output:

17

37302

A backslash multi-line code example within a script.

53464

The Beginning of good things to come
Greater still, the opportunities.

./multiline.sh: line 31: cd: txt: No such file or directory

 txt directory is non-existent yet the next command will still run because the semicolon is used as a separator.

The Beginning of good things to come
Greater still, the opportunities.

A Here tag multi-line code example nested within curly braces, in a script.

3. Conclusion

Linux is remarkable in the multiple ways it can execute a task or desired operation. This is certainly evident in the multiple ways a single command prompt can execute multi-line codes.

In this article, we saw how the backslash allows us to extend commands to new lines. Then, we learned how using parentheses creates a subshell that restricts newly declared variables to itself. We also learned that by using curly braces, we can export newly declared variables to the main shell session. As we saw, the EOF (Here) tag is a multi-line technique with the ability to redirect input in various shells.

We learned that the double ampersand operator is a boolean operator that allows sequential commands to run only upon affirmative execution of prior commands. Then, we saw that using semicolons allows us to run commands in sequence regardless of their return states.

Finally, we saw how to combine these techniques and learned that using scripts is the best way of running complex multi-line shell code.