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

> Learn how to echo output to the terminal from Bash functions in CPython. Discover simple methods for standard output and error streams to enhance your scripts.

- Repository: [Python/cpython](https://github.com/python/cpython)
- Tags: how-to-guide
- Published: 2026-02-21

---

**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`](https://github.com/python/cpython/blob/main/Tools/c-analyzer/must-resolve.sh), functions use standard `echo` commands to communicate progress. The `run_capi` function demonstrates this pattern:

```bash
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`](https://github.com/python/cpython/blob/main/Modules/_decimal/tests/runall-memorydebugger.sh):

```bash
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`](https://github.com/python/cpython/blob/main/Tools/build/regen-configure.sh) follows this convention:

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

```

Similarly, [`Android/android-env.sh`](https://github.com/python/cpython/blob/main/Android/android-env.sh) uses:

```bash
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`](https://github.com/python/cpython/blob/main/Tools/wasm/emscripten/browser_test/run_test.sh) demonstrates this pattern:

```bash
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:

```bash
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:

- **[`Tools/c-analyzer/must-resolve.sh`](https://github.com/python/cpython/blob/main/Tools/c-analyzer/must-resolve.sh)**: Contains `run_capi` and other functions using standard `echo` for progress updates.
- **[`Tools/wasm/emscripten/browser_test/run_test.sh`](https://github.com/python/cpython/blob/main/Tools/wasm/emscripten/browser_test/run_test.sh)**: Uses `tee` for dual logging to terminal and files.
- **[`Tools/build/regen-configure.sh`](https://github.com/python/cpython/blob/main/Tools/build/regen-configure.sh)**: Demonstrates stderr redirection for error messages.
- **[`Modules/_decimal/tests/runall-memorydebugger.sh`](https://github.com/python/cpython/blob/main/Modules/_decimal/tests/runall-memorydebugger.sh)**: Shows decorative echo output for test headers.
- **[`Android/android-env.sh`](https://github.com/python/cpython/blob/main/Android/android-env.sh)**: Simple error echoing to stderr.

## Summary

- Use `echo "message"` inside bash functions for standard output, following the pattern in [`Tools/c-analyzer/must-resolve.sh`](https://github.com/python/cpython/blob/main/Tools/c-analyzer/must-resolve.sh).
- Redirect errors with `echo "error" >&2` to stderr, as seen in [`Tools/build/regen-configure.sh`](https://github.com/python/cpython/blob/main/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`](https://github.com/python/cpython/blob/main/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`](https://github.com/python/cpython/blob/main/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.