• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
NameHero® Blog

NameHero® Blog

Web Hosting Tips & Resources From NameHero

  • AI Agents
    • OpenClaw
    • Claude Code
    • n8n
    • Hermes Agent
    • Open WebUI
    • Docker
  • Hosting
    • Web Hosting
    • WordPress Hosting
    • WooCommerce Hosting
    • Enterprise Hosting
    • Email Hosting
    • HeroicGuard
    • GPU Hosting
    • Website Builder
  • VPS
    • Managed VPS
    • Unmanaged VPS
    • Flex VPS
  • Reseller
  • Gaming
  • Domains
  • Account
  • Blog Home
  • Categories
  • Authors

Bash Debug Mode: set -x and the PS4 Variable

Bhagwad Park

Published on: November 26, 2025

Categories: Linux Command Line, VPS Hosting 0

When coding a program, you usually have a full IDE set up with autocomplete and stack tracing, allowing you to pause execution at any point and see what’s going on under the hood. These tools make coding more productive, and they’ve been in use for a long time. But what if you’re just quickly coding some scripts in bash and don’t have access to the helpful stack traces? Those of us who used to code before IDEs know that the process can be frustrating, with continuous debug statements printed to the standard output. Fortunately, bash has tools to make our lives a bit easier with the “set -x” and PS4 variables, allowing us to see what’s going on under the hood of a script full of variables and expansions.

Let’s see how it works.

Creating a Test Script

In my bash terminal, I’ve created the following script:

Creating the Script and Making it Executable

This is a simple script that prints three lines using a “for” loop that iterates over a sequence from 1 to a defined variable, and then calls a function to print a string that includes that variable. I’ve created the script using the HEREDOC syntax. In the second command, I make the script executable. When I run it, here’s what I get:

Executing the Script

As you can see, the script is a straightforward one. It consists of a function and the main body. The most important part of the script that needs looking at is this one:

for i in $(seq 1 $FILE_COUNT);

The above statement increments the variable “i” by using the “seq” command to count up to the variable $FILE_COUNT. If we’re not getting the output we expect, then we want to see exactly how this statement was resolved, as well as all the statements that used the variable.

Debugging the Script with “set -x”

Now let’s see how we can debug this script using the “set -x” command. At the top of the script, after the first line saying ” #!/bin/bash”, we add the following:

set -x

Now, when we save our changes and run the script again, here’s what we see:

Debugging Statements with Set -x

This time, between the outputs of the function itself, we see several lines starting with a plus (+) sign – this is the default PS4 variable – more on that later.

Whenever a command containing a variable or an expansion is executed, the expansion or variable is evaluated before the command itself. So for the line:

for i in $(seq 1 $FILE_COUNT);

We see that bash has expanded “seq 1 $FILE_COUNT” and made it “seq 1 3”. At the very beginning of the script, bash has printed the variable “FILE_COUNT” as having the value of “3”. From here on out, every command is fully expanded before the output is shown. So before the output:

Inside function: File-2

We can see that bash has first printed:

+ echo 'Inside function: File-2'

Along with the line:

+ local input_var=File-2

This way, we can see clearly which commands were executed to produce the output that we see. When executing commands with multiple expansions, including variables that keep changing, the “set -x” option allows us to see exactly what’s going on when an output doesn’t conform to our expectations. It’s pretty useful to examine the endpoints of loops, which can be tricky.

Customizing the PS4 Variable

The above output is useful, but it can be made even better. The PS4 variable is the one that bash uses to start the output when “set -x” is enabled. By default, we can see that it’s the plus sign (+), but bash allows us to customize it.

Why Customize the PS4 Variable?

While using the “set -x” command to see the actual commands being executed after expansions is very useful, it’s still not enough information for proper debugging. What’s the use of seeing something wrong if you can’t see where it is? The PS4 variable allows us to add “metadata” to scripts along with the command executions, so we can see not only what’s going on, but where.

For example, if we see a command that doesn’t make sense, we’d also like to know the following:

  1. The script name
  2. The line number
  3. The function name

These three things we should know, at the very least. Depending on the context, it might also be useful to know things like the username and the date on which the script was run. The latter two might seem odd, but you can set scripts in such a way that you redirect the “set -x” output to a file for later examination, and the person who runs the script might not be you, but someone else.

To incorporate the above details into our debugging process, we change the PS4 variable like this:

export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '

Now that we’ve modified the PS4 variable, we run the script again. Here’s what we get:

Output with PS4 Variable

In the above screenshot, we’ve utilized some built-in variables to configure the PS4 variable, telling bash to show us the source file, the line number, and the function name. Here, you can see in the highlighted section that the source file is “debug_demo.sh”, the line number is “6”, and the function name is “process_data”. This information is enough for us to pin down exactly what’s going on and where.

For a simple script like the one above, of course, we don’t need to know the file name, and we already know where the statements are being executed. But in a large project, where scripts are calling one another, this information is critical.

Possible Pieces of Information

While I find the above three pieces of information most useful, there are other pieces of data that we can extract as well. Here’s a list of some of the most common special variables we can use while setting the PS4 variable:

  1. ${BASH_SOURCE} – the source file
  2. ${LINENO} – the line number
  3. ${FUNCNAME[0]} – the function name
  4. ${PPID} – the process ID of the parent shell
  5. ${SECONDS} – the number of seconds since the shell was started

Note that while “${FUNCNAME[0]}” is the variable holding the name of the currently executing function, you can also find the name of the function that called it by incrementing the index. So “${FUNCNAME[1]}” will contain the name of the function one level up the stack.

You can combine these two to create a function calling stack. For example, the following command:

export PS4='${FUNCNAME[1]}->${FUNCNAME[0]}: '

Will give this output:

Showing the Parent Function as Well

In the highlighted section, we can see that the calling function “main” called the child function “process_data”. With this information, you now have a full stack trace for the command you want to debug.

Conclusion

Coding scripts in bash usually robs you of your usual dev environment with the IDE, autocorrect, and so on. But that doesn’t mean you’re completely in the dark. Using bash tools like “set -x”, you can get a complete trace of all the commands that are executed, with their expansions. And by setting the PS4 variable, you can see exactly where those commands occur, which functions are responsible for calling and running them, as well as which user generated the output. Using these tools, you have a comprehensive overview of how your script executes, even though it’s not with the same polish and ease of use that you get with a regular IDE.

Bhagwad Park Profile Picture
Bhagwad Park

I’m a NameHero team member, and an expert on WordPress and web hosting. I’ve been in this industry since 2008. I’ve also developed apps on Android and have written extensive tutorials on managing Linux servers. You can contact me on my website WP-Tweaks.com!

Related Posts

Differentiating the df vs du Commands in Linux

Both df and du are Linux commands for estimating disk usage, but they differ in their focus. Df stands for “disk free”, and du stands for disk usage. The first shows the amount of free space available on a volume, and the second shows the amount of space used by specific directories. Both commands have […]

When to Use Here-String vs Heredoc in Bash

Heredoc and Here-string are both ways to input strings into commands that read from stdin. Here's the difference between them.

Linux find with exec: + vs ;

The "find" command is often used with the "exec" command. Here are the two options you can use, and the difference between them.

Curl vs Wget: Choosing the Right Tool for the Job

Linux has two popular tools for getting data from third party websites. Here's the difference between curl and wget.

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

Follow & Subscribe

Exclusive promos, content and more!


Most Popular Posts

NameHero’s Recommended WordPress Plugin and Theme Setup

WordPress Hosting vs. Web Hosting – What’s The Difference?

How To Increase The InnoDB Buffer Pool Size

How To Fix A Stuck All-in-One WP Migration Import

How To Add A Subdomain In Cloudflare

Top Categories

  • WordPress
  • WordPress Tutorials
  • OpenClaw Hosting
  • Enterprise Hosting
  • WooCommerce
  • Web Hosting
  • Resellers
  • Website Security
  • Website Development
  • Website Performance
  • VPS Hosting
  • SEO Tips
  • Announcements
  • Domain Registration
NameHero

NameHero® proudly provides web hosting to over 40,000 customers with 99.9% uptime to over 750,000 websites.

  • Master Card
  • Visa
  • American Express
  • Discover
  • Paypal
Products
  • Web Hosting
  • Managed VPS Hosting
  • Unmanaged VPS Hosting
  • Flex VPS Hosting
  • WordPress Hosting
  • WooCommerce Hosting
  • Reseller Hosting
  • Enterprise Hosting
  • GPU Hosting
  • Email Hosting
  • HeroicGuard
  • Domains
  • Website Builder
  • AI Agent Hosting
Help & Support
  • NameHero Blog
  • NameHero Gaming Blog
  • Support
  • Help Center
  • Migrations
  • Affiliates
  • Gaming Affiliates
  • Call 1-855-984-6263
Company
  • About Us
  • Contact Sales
  • Reviews
  • Uptime
  • We're Hiring

Copyright © 2026 Name Hero, LLC. All rights reserved.
NameHero® is a registered trademark.

  • Privacy Policy
  • Terms of Use
  • Acceptable Use Policy
  • Payment Policy
  • DMCA