Expansion is a useful technique in Bash scripting. This technique is unique and differs from regular programming languages. This allows users to create a huge number of tokens just by writing a few letters. In this article, I will discuss different types of Bash expansion and their implementation while scripting. Hope you will get a clear idea after reading this.
Key Takeaways
- Learning about Bash Expansion.
- Different types of Expansion.
- Basic syntax and implementation of particular Bash expansions.
Free Downloads
What is Bash Expansion?
Expansion in Bash scripting is a handy tool for automating repetitive tasks. Expansion creates multiple tokens based on the original parameter. When you use expansion, the command unfolds across each of the separated tokens. Hence, instead of using the original parameter’s expansion, Bash uses the value created by expanding the rest of the parameter as the new parameter. To have a deeper understanding, go through the various types of expansion discussed later.
Different Types of Expansion in Bash
Bash has different expansions like parameter expansion, brace expansion, arithmetic expansion, tilde expansion etc. Some of these are incredibly useful while others might not be used as often but still good to know. The syntax and implementation of each of the expansion is different. In the next part, I’ll break down a few of these Bash expansions and show you how to use them.
Parameter Expansion in Bash
Parameters are used to store data in Bash script. There are different types of parameters as well such as positional parameter, special parameter and variables. Dollar sign ($) is used for expanding parameters. The basic form of this type of expansion- ${PARAMETER_ NAME}, though curly braces are not mandatory in some cases.
1. Expanding Positional Parameter
Consider a script of adding two numbers given by the user. Within the script, numbers are taken as arguments. To access this one must use the technique of expanding positional parameters. For instance, $1, $2 will expand the first and second argument respectively.
❶ At first, launch an Ubuntu Terminal.
❷ Write the following command to open a addition.sh file in the built-in nano editor:
nano addition.sh
- nano: Opens a file in the Nano text editor.
- sh: Name of the file.
#!/bin/bash
# Assign the positional parameters to variables
echo "This script will add two numbers provided as arguments"
# Access the positional parameters given by the user
num1=$1
num2=$2
# Perform addition
result=$((num1 + num2))
# Display the result
echo "The sum of $num1 and $num2 is: $result"
“#!/bin/bash” is called “shebang” that specifies the Bash interpreter. Then, it expands the first and second positional parameters using $1 and $2 and assigns to the variables num1 and num2 respectively.
Next, it calculates the sum of num1 and num2 using arithmetic expression syntax and stores it in the result variable. Finally, it displays the result to the user.
❹ Use the following command to make the file executable:
chmod u+x addition.sh
- chmod: Changes permissions.
- u+x: Giving the owner executing permission.
- sh: Name of the script.
./addition.sh
As it shows the script takes two positional arguments such as 10 and 20. Then it calculates the sum of the given arguments.
2. Expanding Variable and Its Properties
There are a couple of shell parameters that store information about variables. Let’s visualize those one by one. First of all, ${variable} will expand the value of the variable.
name="Anita"
echo ${name} #Expands the variable
To count the length of a variable expand the variable with “#” prefix.
my_string="Hello, World!"
echo ${#my_string} #Count the length of the variable.
If a variable is not set then you can set the variable using the syntax ${var_name:=value}.
echo $name
echo ${name:=Anita}
Look at the list below. It contains other variable related parameter expansion available in Bash.
Parameter Expansion | Description | Example |
---|---|---|
${variable:-value} | If the variable is unset or undefined, expand to the specified value. | myvar=”” echo ${myvar:-“Default”} # Output: Default |
${variable:+value} | If the variable is set or defined, expand to the specified value. | myvar=”Hello” echo ${myvar:+”World”} # Output: World |
${variable:start:length} | Extract a substring from the variable starting at the given position with the specified length. | text=”Hello, World!” echo ${text:7:5} # Output: World |
${variable:start} | Extract a substring from the variable starting at the given position to the end. | text=”Hello, World!” echo ${text:7} # Output: World! |
${variable/pattern/string} | Replace the first occurrence of the pattern in the variable with the specified string. | sentence=”I love apples.” echo ${sentence/apples/oranges} # Output: I love oranges. |
${variable//pattern/string} | Replace all occurrences of the pattern in the variable with the specified string | sentence=”I love apples and apples are great.” echo ${sentence//apples/oranges} # Output: I love oranges and oranges are great. |
${variable/#pattern/string} | If the pattern exists at the beginning of the variable, replace it with the specified string. | text=”apple pie” echo ${text/#apple/cherry} # Output: cherry pie |
${variable/%pattern/string} | If the pattern exists at the end of the variable, replace it with the specified string. | text=”apple pie” echo ${text/%pie/cake} # Output: apple cake |
${variable#pattern} | Remove the shortest match from the beginning of the variable where the pattern matches. | path=”/path/to/file.txt” echo ${path#*/} # Output: path/to/file.txt |
${variable##pattern} | Remove the longest match from the beginning of the variable where the pattern matches. | path=”/path/to/file.txt” echo ${path##*/} # Output: file.txt |
${variable%pattern} | Remove the shortest match from the end of the variable where the pattern matches. | path=”/path/to/file.txt” echo ${path%/*} # Output: /path/to |
3. Command Substitution
Command substitution is another parameter expansion that begins with a dollar sign ($). However, following the dollar sign first bracket is used to expand the command. “$(Command_Name)” is the basic syntax for substituting commands.
For instance, substitute the date command and save the output in a variable.
today=$(date)
echo $today
Here, the date command is substituted and output is saved in the today variable.
Brace Expansion in Bash
Brace expansion is a powerful feature in Bash scripting. Curly braces “{}” are used for shell brace expansion. A basic example of shell brace expansion for a list can be as follows:
echo {Monday,Tuesday,Wednesday,Thursday,Friday}
Here, curly brace expands each element of the list separated by a comma. The output of the echo command verifies the expansion.
You can also define a range to generate a sequence using brace expansion. The start and end of the range is separated by two dots (..)
echo {1..5}
Moreover, you can utilize brace expansion to generate a sequence within a range with positive or negative increment. For instance, to generate a sequence of numbers from 1 to 15 with positive increment two type the following command in the Terminal.
echo {1..15..2}
Finally, brace expansion has nested use as well. This can be so powerful if perfectly used. An example of nested brace expansion looks like this.
echo {{A,B}{1,2}}
Syntax of multiple nested brace expansion looks like this:
echo {{A,B}{1,2},{X,Y}{3,4}}
Here, {A,B}{1,2} and {X,Y}{3,4} are two different nested brace structures.
Arithmetic Expansion
Arithmetic expansion is a useful feature in Bash that allows users to perform arithmetic calculations within a script. To enable arithmetic expansion simply enclose the expression to be evaluated within double parentheses. See the example of simple addition of 2 and 3.
echo $((2+3))
Let’s evaluate a more complex expression and update variable values with the result.
result=0
result=$(( (5 + 3) * (10 - 2) / 4 ))
echo $result
Arithmetic expansion can also handle bitwise operations. The syntax for bitwise operation is “&”. On the other hand, syntax for regular multiplication is “*”
x=5
y=3
echo $((x & y))
Bash Tilde Expansion
HOME is an environment variable that typically stores the path of a directory. This is called the home directory. Tilde(~) basically expands to the home directory or whatever is defined in the HOME variable. If the HOME variable is not set then it expands to the home directory of the current user.
Let’s run the command to see what’s stored in the HOME variable.
echo $HOME
It shows /home/laku the current value of the HOME variable. Now, see how tilde(~) expands the path set in the HOME variable.
cd ~/Desktop/
The command cd is used to change the current working directory.The “~/Desktop/” portion represents an absolute path, indicating a directory named “Desktop” within the HOME directory. The program expands the HOME directory by the tilde sign (~) referenced before the Desktop directory.
At this point, Let’s have a deeper look. What will happen if I change the value of the HOME variable! For instance, I want to set “/usr/local” as the new value of the HOME variable.
export HOME=/usr/local
Once you run this command, this changes the HOME variable. You can see the effect instantly in the terminal as the previous tilde sign is gone and the full path of the current directory becomes visible. The “/home/laku” directory may be the user’s home directory however, this is not the path referenced by the tilde sign (~) as the value of the environment HOME variable is changed to “/usr/local”.
cd ~
Now run the above command to expand the new HOME variable using the tilde sign (~).Current directory changes after executing the command. To see my current directory I run the pwd command. It shows “/usr/local” as the current working directory. It is expected as tilde (~) takes me to “/usr/local” because currently it is set in the HOME variable.
Bash History Expansion
Expertise in accessing the history makes life easier for command line users. You can recall previously executed commands and arguments instead of writing them again. I am going to show you a few tricks related to history expansion.
First of all, to access and re-execute the last command use double exclamation (!!).
!!
# Output: Return and execute the last command
To repeat the n-th number of commands from the history list use “!n”. For example, to get the first command from the history list run “!1”
!1
# Output: Return and execute 1st command from the history list
Now, if you want to get back the most recent command that starts with a specific string, then write that string after the exclamation.
!da
# Output: Return and execute last command that starts with da
For getting back the n-th argument of the last command use the “!!:n” syntax.
echo one two three
echo !!:2
# Output: echo two
To replace a certain string in the last executed command use “^old^new^” syntax. See the example to have a clear idea.
echo This is an example.
^example^sample^
Here, the string “example” is replaced with “sample” in the last executed echo command.
Wildcard Expansion and Globbing in Bash
Wildcard expansion in some cases known as globbing is a feature that allows users to use some special characters or set of characters to match patterns. These special characters are known as wildcard characters. Examples of such characters are “*”,”?”,”!” etc. These characters are super helpful in finding or matching filenames or directories. The process of finding files or directories using wildcards is called globbing.
Let’s explore how to utilize wildcard characters. First of all, asterisk(*) matches a single or more characters. To give an example, consider a directory filled with files of a website. From the huge volume of files, let’s say I want to extract files with .md extension. The following command will do the task in seconds.
find -type f -name "*.md"
Here, the wildcard(*) matches any character before “.md” and finds the files shown in the image.
Apart from asterisk(*), there are many other wildcards available in Bash. The below list contains useful wildcards and their function in Bash scripting.
Wildcard Syntax | Function | Example | Explanation |
---|---|---|---|
* | Match one or more characters | *.txt | Match with anything that ends with “.txt” |
? | To match a single character | image?.jpg | Can match with “image1.jpg“, “imageA.jpg“, but not with “image12.jpg“ |
! | Match any single character not in the specified range | [!0-9] | Match any single character that is not a digit |
^ | Matches single character that is not specified within square brackets. | [^123] | Match with any single character other than “1”, “2”, or “3” |
| | Match with characters of one of the sides of the pipe (|) sign | file(a|b).txt | Match “filea.txt” or “fileb.txt“ |
[:alpha:] | Match any uppercase or lowercase letter | file[:alpha:].txt | Match with “filea.txt” or “fileZ.txt” but not “file1.txt“ |
[:digit:] | Match any digit within 0 to 9 | file[:digit:].txt | Match with “file1.txt” or “file0.txt” but not “filea.txt“ |
[:alnum:] | Match any letter from A to Z case-insensitively along with digits | file[:alnum:].txt
|
Match with “file1.txt” or “file0.txt” but not “filea.txt“ |
[:upper:] | Match the uppercase letter only from A to Z | file[:upper:].txt | Match with “fileA.txt” or “fileB.txt” but not “filea.txt“ |
[:lower:] | Match with the lowercase letter only from a to z | file[:lower:].txt | Match with “filea.txt” or “fileb.txt” but not “fileZ.txt“ |
[letters] | Match any letter within the square bracket | [aeiou] | Match any single character that is vowels a, e, i, o, or u. |
[:space:] | Match with white space | ||
[:blank:] | Match with space or tab |
nullglob is an option in Bash shell that defines whether the shell will change the globbing pattern as an empty string or leave it as it is. When enabled the pattern that doesn’t match with any filename turns to be a null string. Otherwise, the shell leaves the pattern unchanged as written in the command. To enable and disable the option follow the code below:
shopt -s nullglob
# enable
shopt -u nullglob
# disable
Array Expansion in Bash
Array expansion is quite useful to access and manipulate array elements within a Bash script. Expanding an array offers us accessing a particular element or looping through all the elements using a loop structure. “[@]” syntax is used to expand an array.
Let’s declare and define an array called arr1.
declare -a arr1
arr1=("element1" "element2" "element3")
echo "${arr1[@]}"
Next, the declared arr1 is initialized using “arr1=(“element1” “element2” “element3”)” this code. This actually makes arr1 an array with three elements.
Finally, ${arr1[@]} expands the entire arr1 and prints each element as a separate string using echo command.
declare -a arr1
arr2=("Ubuntu" "Redhat" "Kali Linux")
echo "${arr2[*]}"
Next, the declared arr2 is initialized using “arr2=(“Ubuntu” “Redhat” “Kali Linux”)” this code. This actually makes arr2 an array with three different elements.
Finally, ${arr2[*]} expands the entire arr2 and prints each element as a single string using echo command.
Looping through an array is the main purpose of expanding an array. Let’s see how to incorporate a loop structure while expanding an array.
Script (arrayexpand.sh) >
#!/bin/bash
arr3=(15 10 5)
for item in "${arr3[@]}"; do
echo "$item"
done
Conclusion
In conclusion, there are different types of Bash expansion. Each of them is quite useful while scripting. I believe after reading this article, you have an overall idea about Bash expansion. Follow our articles to learn more about each type of expansion.