How to Echo Output to the Terminal from Within a Bash Function in CPython

Use echo "message" for standard output or echo "error" >&2 for stderr inside the function body, following the conventions established in CPython's build and test scripts.

The CPython repository relies heavily on Bash scripts for build automation, testing, and tooling. When contributing to Python's development or modifying its build system, you'll often need to display diagnostic messages from within shell functions. Understanding how to properly echo output to the terminal from within a bash function ensures your scripts remain consistent with CPython's established conventions.

Basic Echo Syntax Inside Bash Functions

In CPython scripts like Tools/c-analyzer/must-resolve.sh, functions use standard echo commands to communicate progress. The run_capi function demonstrates this pattern:

function run_capi() {
    echo "Running C API tests..."
    # Additional test logic

}

This sends output to stdout, allowing the terminal to display the message immediately while preserving the output for potential capture by parent scripts.

Directing Output to stdout vs stderr

CPython's build scripts distinguish between informational messages and error conditions by choosing the appropriate output stream.

Standard Output for Informational Messages

Use unredirected echo for standard progress updates, as seen in Modules/_decimal/tests/runall-memorydebugger.sh:

echo "# ========================================================================"

echo "# Running memory debugger tests"

echo "# ========================================================================"

Standard Error for Warnings and Errors

Redirect to stderr when reporting configuration errors or warnings. The script Tools/build/regen-configure.sh follows this convention:

echo "$@ needs either Podman or Docker container runtime." >&2

Similarly, Android/android-env.sh uses:

echo "$1" >&2

This separation allows build systems to filter errors distinctly from standard build logs.

Advanced Logging Techniques in CPython Scripts

For complex build scenarios, CPython scripts employ additional techniques to handle output formatting and dual logging.

Logging to File and Terminal Simultaneously with tee

When you need to display output in the terminal while preserving it in a log file, use tee. The WebAssembly test script Tools/wasm/emscripten/browser_test/run_test.sh demonstrates this pattern:

function build_wasm() {
    echo "Starting WebAssembly build…" | tee -a build.log
    # Build steps …

    echo "WebAssembly build finished." | tee -a build.log
}

The -a flag appends to the log file rather than overwriting it.

Formatted Output with printf

For precise formatting control, use printf instead of echo. This avoids issues with trailing newlines and provides format specifiers:

function report_stats() {
    local tests=$1 passed=$2 failed=$3
    printf "Tests run: %d, Passed: %d, Failed: %d\n" "$tests" "$passed" "$failed"
}

Key Files in the CPython Repository

The following files demonstrate these echo patterns in production CPython scripts:

Summary

  • Use echo "message" inside bash functions for standard output, following the pattern in Tools/c-analyzer/must-resolve.sh.
  • Redirect errors with echo "error" >&2 to stderr, as seen in Tools/build/regen-configure.sh.
  • Log to both terminal and files using echo "msg" | tee -a logfile, demonstrated in WebAssembly build scripts.
  • Use printf for formatted output when precise control over spacing and newlines is required.
  • Always match the output stream (stdout vs stderr) to the message severity to maintain compatibility with CPython's build system expectations.

Frequently Asked Questions

What is the difference between echo and printf in bash functions?

echo provides a simple way to output text with an automatic newline, but behavior can vary across systems regarding special characters. printf offers consistent formatting across platforms, supports format specifiers like %s and %d, and gives you explicit control over newlines. In CPython scripts, echo is preferred for simple messages, while printf appears when formatting numerical test results or aligned columns.

How do I suppress output from a bash function in CPython build scripts?

Redirect output to /dev/null using echo "message" > /dev/null for stdout or echo "message" 2>/dev/null for stderr. To suppress both, use echo "message" &>/dev/null. This technique is useful when calling verbose helper functions where you only care about the return code, not the diagnostic text, keeping the build output clean according to CPython's build system conventions.

Can I use echo to write to both stdout and a log file without using tee?

While tee is the standard approach in CPython scripts, you can manually redirect output using file descriptors: echo "message" | tee -a logfile or echo "message" >> logfile; echo "message". However, the single-command tee approach shown in Tools/wasm/emscripten/browser_test/run_test.sh is preferred because it is atomic, handles both destinations simultaneously, and is less prone to desynchronization between the terminal and log file.

When should I use stderr instead of stdout in CPython bash functions?

Use stderr for error messages, warnings, and diagnostic information that should not be captured as part of the normal output stream. As implemented in Tools/build/regen-configure.sh, error messages redirect to stderr so that build systems can separate them from successful build logs. Use stdout for expected, normal output such as progress indicators, success confirmations, or data that might be piped to other commands in the build pipeline.

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 →