1. Overview
Printing the exact commands in a script before execution can be really useful. In this tutorial, we’ll learn how to make Bash scripts echo commands before they’re executed.
2. The set -x or set -o xtrace Option
The set -x option is one of the methods we can use to see what’s going on in Bash scripts. Let’s now see some ways we can use this option.
2.1. Adding set -x to a Script
When we add set -x inside scripts, it echoes any command that comes after set-x. Also, the set -x option expands variables. We can turn off the effect of this option using set +x:
#!/bin/bash
echo "file operation begins..."
set -x
mkdir new_directory
cd new_directory
touch data.txt
ls $PWD
set +x
echo "finished!"
Let’s run the script:
$ ./example_1.sh
file operation begins...
+ mkdir new_directory
+ cd new_directory
+ touch data.txt
+ ls /new_directory
data.txt
+ set +x
finished!
Here, we add set -x before some commands. The shell prints these commands with a + sign before their actual execution.
Also, as we can see in the output, set -x expands the $PWD variable to /new_directory. Then we turned off the feature using set +x.
2.2. Using -x in a Shebang Line
When we add -x to the shebang line of a script, it echoes each command before execution. With this, we have a full view of commands a script executes:
$ cat example_2.sh
#!/bin/bash -x
name="John"
age=25
echo "Name: $name"
echo "Age: $age"
if [ "$age" -ge 18 ]; then
echo "You're an adult."
else
echo "You're not an adult."
fi
echo "Script execution complete."
The script produces the output:
$ ./example_2.sh
+ name=John
+ age=25
+ echo 'Name: John'
Name: John
+ echo 'Age: 25'
Age: 25
+ '[' 25 -ge 18 ']'
+ echo 'You're an adult.'
You're an adult.
+ echo 'Script execution complete.'
Script execution complete.
Also, we can use set +x to stop printing the commands.
2.3. Running Scripts Using bash -x
Let’s say we don’t want to edit a script. Using bash -x, we can still enable command echoing directly from the command line:
$ cat example_3.sh
#!/bin/bash
greet() {
echo "Hello, $1!"
}
user="Bob"
greet "$user"
date
ls
uptime
echo "Script execution complete!"
Let’s run the script:
$ bash -x example_3.sh
+ user=Bob
+ greet Bob
+ echo 'Hello, Bob!'
Hello, Bob!
+ date
Mon Apr 1 07:36:15 UTC 2024
+ ls
data.txt new_directory new_script.sh processed website_status.txt
+ uptime
07:36:15 up 2:40, 0 users, load average: 0.31, 0.43, 0.48
+ echo 'Script execution complete!'
Script execution complete!
The bash -x command prints every command in the script, even though -x isn’t set within the script.
3. The set -v or set -o verbose Option
The set -v option does the same thing as set -x. But, unlike set -x, it doesn’t print a + sign before the command. Also, it doesn’t expand the variables like we saw in the previous option.
3.1. Enabling set -v Within a Script
We can enable set -v within a specific section of the script to help us debug better. Also, set +v disables verbose output:
$ cat example_4.sh
#!/bin/bash
echo "This message will be printed."
set -v
echo "This line will be shown before it runs."
ls
set +v
echo "This message will be printed normally."
Let’s run the script:
$ ./example_4.sh
This message will be printed.
echo "This line will be shown before it runs."
This line will be shown before it runs.
ls
data.txt new_directory new_script.sh processed website_status.txt
set +v
This message will be printed normally.
The set -v command within the script echoes every command that comes after it. So, it prints the next lines before execution. Also, the set +v command stops the printing of commands.
3.2. Using -v in a Shebang Line
When we add -v to the shebang line at the top of a Bash script, it prints every command in the script before execution:
$ cat example_5.sh
#!/bin/bash -v
echo "Checking for updates..."
sudo apt update
echo "Installing new packages..."
sudo apt upgrade -y
echo "Script complete!"
We get something similar to the output below after running the script:
./example_5.sh
#!/bin/bash -v
echo "Checking for updates..."
Checking for updates...
sudo apt update
...
echo "Installing new packages..."
Installing new packages...
sudo apt upgrade -y
...
echo "Script complete!"
Script complete!
The shebang line #!/bin/bash -v tells Bash to print every command in it. So, the script prints each line before execution.
3.3. Running Scripts Using bash -v
We can make a script to print every command without editing the script itself. The script can print commands using bash -v directly on the command line:
$ cat example_6.py
#!/bin/bash
echo "Starting file operations..."
mkdir new_directory
cd new_directory
touch example_file.txt
ls
echo "File operations complete!"
Let’s find out what the script produces:
$ bash -v example_6.sh
#!/bin/bash
echo "Starting file operations..."
Starting file operations...
mkdir new_directory
cd new_directory
touch example_file.txt
ls
example_file.txt
echo "File operations complete!"
File operations complete!
The example above tells us that the script prints each command before execution.
4. Custom echo Function
Let’s create a custom echo function that prints each command before its execution. It can be pretty helpful when we want to have more control over what’s printed:
$ cat example_7.sh
#!/bin/bash
echo_cmd() {
echo "Executing: $@"
"$@"
}
echo_cmd echo Hello, World!
echo_cmd ls -l
echo_cmd pwd
When we run the command above, we’ll get:
./example_7.sh
Executing: echo Hello, World!
Hello, World!
Executing: ls -l
total 28
-rw-r--r-- 1 root root 0 Apr 1 06:31 data.txt
drwxr-xr-x 2 root root 4096 Apr 1 13:25 new_directory
-rwxr-xr-x 1 root root 113 Apr 2 08:47 new_script.sh
drwxr-xr-x 2 root root 4096 Apr 1 05:57 processed
-rw-r--r-- 1 root root 15757 Apr 1 06:50 website_status.txt
Executing: pwd
/new_directory
The custom echo function starts by printing, “Executing: ” followed by the entire command line it receives, before executing it.
5. Conclusion
In this article, we learned about the set -x, set -v, and a custom echo function. We saw how they help print commands in Bash scripts during execution.
With this understanding, we have a useful tool that can help us debug issues in Bash scripts.