Expansion
When we type a command with arguments/inputs and press the enter key, the shell
does several things to the arguments/input text before it actually carries out
the command. This action is called expansion. With expansion, the arguments
expands into something else before the shell acts on it with the command. Let's
see an example with echo
command which prints out the text arguments given on
standard output.
echo hello world!
hello world!
Let's use echo
with *
:
echo *
hello.c hello.qsub hello.sb README
Instead of printing *
, it prints out all file names in the directory because
shell expands *
into something else before the echo
command acts on the
argument (in this case *
).
ls
hello.c hello.qsub hello.sb README
The *
character is called a "wildcard" is replaced by everything in the
current directory. We can also modify it so that it's only replaced by certain
things. For example, h*
is replaced everything that starts with h
in the
current directory.
echo h*
hello.c hello.qsub hello.sb
~
is another special character with a special meaning. It expands
into the name of the home directory of the user:
echo ~
/mnt/home/temp_user_01
The shell will also expand arithmetic. Arithmetic expansion uses the form $((expression))
. Look at the example.
echo $((1 + 1))
2
Please keep in mind that arithmetic expansions allows only integers. Arithmetic expression can be nested, and spaces are allowed.
echo $(( 7*( 2 + 2 ) ))
28
echo $((7*(2+2)))
28
Brace expansion is useful when you write a shell script or batch script. The brace expression can contain a comma separated list of characters, strings, or integers that will be expanded into multiple expressions. Here are a few examples.
echo srt-{a,b,c}-end
srt-a-end srt-b-end srt-c-end
echo number_{1..5}
number_1 number_2 number_3 number_4 number_5
echo {A..Z}
``` output title="output"
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
echo a{A{1,5},B{6..10}}b
aA1b aA5b aB6b aB7b aB8b aB9b aB10b
The next example creates multiple directories with a brace expansion.
mkdir Photos
cd Photos/
mkdir {2020..2021}-{01..12}
ls
2020-01/ 2020-03/ 2020-05/ 2020-07/ 2020-09/ 2020-11/ 2021-01/ 2021-03/ 2021-05/ 2021-07/ 2021-09/ 2021-11/
2020-02/ 2020-04/ 2020-06/ 2020-08/ 2020-10/ 2020-12/ 2021-02/ 2021-04/ 2021-06/ 2021-08/ 2021-10/ 2021-12/
Expansion also allows us to use the output of a command in different places.
The syntax is the same as arithmetic expansion, except the command is placed
inside $(...)
echo $(ls | grep 2020)
2020-01/ 2020-02/ 2020-03/ 2020-04/ 2020-05/ 2020-06/ 2020-07/ 2020-08/ 2020-09/ 2020-10/ 2020-11/ 2020-12/
Next, let's learn how to control expansion. Consider the following two examples where automatic expansion doesn't do what we want.
echo This is a test
This is a test
echo The total is $100.00
The total is 00.00
In the first example, the shell removes extra space from the echo
command's
argument. In the second example, $1
is interpreted as the first input
parameter which is not defined here, and therefore, it is replaced as empty
string. With quoting, we can suppress unwanted expansions.
First, let's learn about double quotes. If we place text inside double quotes,
parameters are not split by white space and all special characters lose their
special meaning, and are treated as ordinary characters. However $
, \
(backslash), and `
(back quote) are exceptions.
echo "This is a test"
This is a test
echo "The total is $100.00"
The total is 00.00
Here is another example where the shell splitting the parameters by white space
would cause a problem. Suppose that our working directory has the file two
words.txt
in it, where the filename has a space.
ls two words.txt
ls: cannot access two: No such file or directory
ls: cannot access words.txt: No such file or directory
The shell splits the two words and looks for them each separately with ls
.
Quoting fixes this.
ls "two words.txt"
two words.txt
If you want to suppress all expansions, you need to use single quotes.
echo 'This is a test'
This is a test
echo 'The total is $100.00'
The total is $100.00
The next three examples show how quoting gives different results.
echo text ~/*.txt {1..5} $(echo foo) $((2+2)) $(date)
text /mnt/home/user_name/hostfile.txt /mnt/home/user_name/powertools.txt
1 2 3 4 5 foo 4 Tue Jan 19 15:10:00 EST 2021
echo "text ~/*.txt {1..5} $(echo foo) $((2+2)) $(date)"
text ~/*.txt {1..5} foo 4 Tue Jan 19 15:10:09 EST 2021
echo 'text ~/*.txt {1..5} $(echo foo) $((2+2)) $(date)'
text ~/*.txt {1..5} $(echo foo) $((2+2)) $(date)
A backslash is useful when we want to quote a single character. A backslash is called the escape character. Next example shows how quoting and an escape character work.
echo The balance of $(date) is $100
The balance of Tue Jan 19 15:15:36 EST 2021 is 00
echo "The balance of $(date) is $100"
The balance of Tue Jan 19 15:15:48 EST 2021 is 00
echo 'The balance of $(date) is $100'
The balance of $(date) is $100
echo "The balance of $(date) is \$100"
The balance of Tue Jan 19 15:16:09 EST 2021 is $100
echo 'The balance of $(date) is \$100'
The balance of $(date) is \$100
The table show the most frequently used escape characters.
Escape Character | Name | usage |
---|---|---|
\n | newline | Adding blank lines to text |
\t | tab | Inserting horizontal tabs to text |
\a | alert | Making the user terminal beep |
\\ | backslash | Inserting a backslash |