How to Use Bash Echo to Echo Shell Commands as They Execute

Use set -x to enable execution tracing, which prints each command to standard error with a + prefix before Bash executes it.

When debugging complex shell scripts, seeing exactly which commands run and in what order is invaluable. While many developers search for ways to use bash echo to echo shell commands, the shell actually provides a built-in execution tracing mechanism that outputs commands automatically as they execute. This feature eliminates the need to manually add echo statements throughout your code.

Understanding Bash Execution Tracing

Bash execution tracing is a built-in debugging feature, not an external command. When active, the shell prints each command to standard error (stderr) after performing parameter expansion, command substitution, and arithmetic expansion, but before executing the command.

The trace output uses a prefix string defined by the PS4 variable, which defaults to + . This makes the output appear similar to manually inserted echo statements, hence the common search for "bash echo" solutions.

Enabling Command Echoing with set -x

The most common way to enable execution tracing is with the set built-in command.

Enable Tracing in a Script

Place set -x at the beginning of your script to trace all subsequent commands:

#!/usr/bin/env bash

set -x

echo "Starting backup process"
tar -czf backup.tar.gz /home/user/documents
echo "Backup complete"

Enable Tracing for Specific Blocks

Use set +x to disable tracing for sections that produce too much noise:

#!/usr/bin/env bash

set -x

echo "This command is traced"

set +x
echo "This command is not traced"
echo "Neither is this one"
set -x

echo "Tracing resumes here"

Invoke Bash with Tracing Enabled

You can also start a script with tracing already enabled without modifying the script:

bash -x script.sh

Customizing Trace Output

Bash provides two key variables for controlling how execution traces appear: PS4 and BASH_XTRACEFD.

Customizing the Prefix with PS4

The PS4 variable defines the string printed before each trace line. You can customize it to include timestamps, line numbers, or source file names.

#!/usr/bin/env bash

# Include source file, line number, and function name

export PS4='+ ${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x

echo "This trace shows exactly where the command is located"

Common PS4 expansions include:

  • ${LINENO} — Current line number in the script
  • ${BASH_SOURCE} — Name of the current source file
  • ${FUNCNAME[0]} — Current function name
  • \t — Tab character for alignment

Redirecting Trace Output with BASH_XTRACEFD

By default, traces go to stderr. Use BASH_XTRACEFD to redirect traces to a specific file descriptor, allowing you to log traces separately from error output.

#!/usr/bin/env bash

# Open file descriptor 5 for writing to a log file

exec 5>command_trace.log
export BASH_XTRACEFD=5

set -x

echo "This command appears in the log file, not on screen"
ls -la

Practical Examples

Debugging a Deployment Script

#!/usr/bin/env bash

set -e  # Exit on error

set -x  # Enable tracing

echo "Deploying application..."
rsync -avz ./build/ user@server:/var/www/app/
ssh user@server "sudo systemctl restart nginx"

set +x
echo "Deployment complete! Check the server logs for details."

Conditional Tracing Based on Debug Mode

#!/usr/bin/env bash

DEBUG=${DEBUG:-0}

if [[ "$DEBUG" -eq 1 ]]; then
    set -x
    export PS4='+ [DEBUG] ${BASH_SOURCE}:${LINENO}: '
fi

echo "Running in ${DEBUG:+debug }mode"

Summary

  • Use set -x to enable execution tracing, which prints each command to stderr before execution with a + prefix.
  • Use set +x to disable tracing for specific code blocks that generate excessive output.
  • Customize PS4 to include contextual information like line numbers, source files, and timestamps in trace output.
  • Set BASH_XTRACEFD to redirect trace logs to a file descriptor other than stderr for separate logging.
  • Invoke with bash -x script.sh to enable tracing without modifying the script file.

Frequently Asked Questions

How do I echo shell commands in Bash without using the echo command?

Use the built-in execution tracing feature by running set -x in your script or invoking Bash with the -x flag. This prints each command to standard error before execution, effectively echoing the command without manually adding echo statements throughout your code.

What is the difference between set -x and set -v in Bash?

set -x prints commands after parameter expansion, command substitution, and arithmetic expansion, showing the actual values being executed. set -v prints input lines as they are read, before any processing occurs. Use -x for debugging what actually runs, and -v to see the raw script input including comments and unexpanded variables.

How can I redirect Bash trace output to a file instead of stderr?

Set the BASH_XTRACEFD variable to a file descriptor number, then open that descriptor for writing to your log file. For example: exec 5>trace.log; export BASH_XTRACEFD=5; set -x. This sends all trace output to trace.log while keeping stderr clean for actual error messages.

Can I customize the prefix shown before each traced command?

Yes, modify the PS4 environment variable to change the trace prefix from the default + to any string you prefer. You can include dynamic information using parameter expansion such as ${LINENO} for line numbers, ${BASH_SOURCE} for filenames, or ${FUNCNAME[0]} for function names to create more informative debug output.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →