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.