How to Convert Text to Speech in Bash

How to Convert Text to Speech in Bash

A while back, I wanted a way to have my terminal read notifications and log summaries out loud while I was working on something else across another monitor. I didn’t want to build a whole app for it, so I put together a simple Bash text-to-speech script using existing command-line tools. It turned out to be far more useful than I expected, and I still use a version of it today for accessibility testing and hands-free notifications. Here’s how I built it.

Why Do Text-to-Speech from Bash

Bash itself has no built-in speech synthesis, but it’s an excellent orchestration layer for tools that do. Building a TTS script in Bash gives you:

  • The ability to trigger speech output as part of any script or automation (build completion, error alerts, reminders).
  • Easy integration with cron jobs, monitoring scripts, or accessibility tooling.
  • A lightweight, dependency-light way to add audio feedback to command-line workflows.

The Tools Behind Bash TTS

Bash relies on external TTS engines; the two most common are:

  • espeak / espeak-ng — A lightweight, offline, open-source speech synthesizer available on most Linux distributions.
  • festival — Another open-source TTS engine, often with more natural-sounding voices than espeak.
  • say — macOS’s built-in text-to-speech command, no installation required.
  • Cloud APIs (Google Cloud TTS, Amazon Polly) — Higher quality, natural-sounding voices, but require API keys and network access.

Installing a TTS Engine

On Debian/Ubuntu:

sudo apt update
sudo apt install espeak-ng

On Fedora:

sudo dnf install espeak-ng

On macOS, say is already built in — no installation needed.

The Simplest TTS Script

#!/bin/bash

espeak-ng "Hello, this is your terminal speaking."

That’s genuinely all it takes to hear synthesized speech from a script. On macOS, the equivalent is:

say "Hello, this is your terminal speaking."

A Cross-Platform Wrapper Script

Since I work across both Linux and macOS machines, I built a wrapper that detects the operating system and picks the right TTS command automatically:

#!/bin/bash

set -euo pipefail

speak() {
    local text="$1"

    if command -v say >/dev/null 2>&1; then
        say "$text"
    elif command -v espeak-ng >/dev/null 2>&1; then
        espeak-ng "$text"
    elif command -v espeak >/dev/null 2>&1; then
        espeak "$text"
    else
        echo "Error: no supported text-to-speech engine found." >&2
        exit 1
    fi
}

speak "$1"

How This Works Internally

  • command -v say >/dev/null 2>&1 checks whether the say command exists on the system without printing anything to the terminal, redirecting both stdout and stderr to /dev/null.
  • The if/elif chain tries macOS’s say first, then falls back to espeak-ng, then plain espeak, and finally exits with an error if none are available.
  • Wrapping the logic in a speak() function makes it reusable across the rest of the script or in other scripts that source this file.

Adding Voice, Speed, and Pitch Controls

espeak-ng supports a range of options for customizing the voice output:

#!/bin/bash

set -euo pipefail

text="$1"
speed="${2:-150}"   # words per minute
pitch="${3:-50}"    # 0-99

espeak-ng -s "$speed" -p "$pitch" "$text"
  • -s controls speaking rate in words per minute (default is around 175).
  • -p controls pitch, ranging from 0 (lowest) to 99 (highest).

You can list available voices with:

espeak-ng --voices

And use a specific voice with:

espeak-ng -v en-us+f3 "This is a female English voice."

Converting Text Files to Speech and Saving as Audio

Rather than speaking directly, you can also render speech to a .wav file for later playback or sharing:

#!/bin/bash

set -euo pipefail

input_file="$1"
output_file="${2:-output.wav}"

espeak-ng -f "$input_file" -w "$output_file"

echo "Audio saved to $output_file"
  • -f "$input_file" tells espeak-ng to read input text from a file instead of a command-line argument.
  • -w "$output_file" writes synthesized speech to a WAV audio file rather than playing it immediately.

Reading Long Documents Aloud, One Paragraph at a Time

For longer text, I like breaking output into paragraph chunks so I can pause, skip, or replay a section without restarting the whole thing:

#!/bin/bash

set -euo pipefail

input_file="$1"

while IFS= read -r -d '' paragraph; do
    echo "----"
    echo "$paragraph"
    espeak-ng "$paragraph"
    read -rp "Press Enter for next paragraph, or 'q' to quit: " cmd
    [[ "$cmd" == "q" ]] && break
done < <(awk -v RS='' '{print; print "\0"}' "$input_file")

This uses awk with RS='' (paragraph mode, where blank lines separate records) to split the file into paragraphs, then reads them one at a time using a null-delimited read loop, pausing for user input between each.

Real-World Use Cases

  • Accessibility tools: Reading terminal output or log messages aloud for visually impaired users or hands-free workflows.
  • Build and deployment notifications: Announcing “Build succeeded” or “Build failed” audibly so you don’t need to keep glancing at a terminal.
  • Language learning: Converting vocabulary lists or sentences into spoken audio for pronunciation practice.
  • Long-form reading: Converting articles or documentation into audio to listen to while doing other tasks.
  • Alerting systems: Speaking critical system alerts (disk almost full, service down) out loud on a server room speaker.

Automation Example

Here’s a script I use to have my machine announce when a long-running backup job finishes:

#!/bin/bash

set -euo pipefail

backup_source="/data"
backup_dest="/backups/backup_$(date +%F).tar.gz"

tar -czf "$backup_dest" "$backup_source"

if [ $? -eq 0 ]; then
    espeak-ng "Backup completed successfully."
else
    espeak-ng "Warning. Backup failed. Please check the logs."
fi

This way, I can start a backup and walk away, trusting I’ll hear whether it succeeded or failed instead of babysitting a terminal window.

Best Practices

  • Always check whether a TTS engine is installed before attempting to use it, and fail gracefully with a clear error message if not.
  • Keep spoken messages short and clear — TTS engines can mangle long, complex sentences.
  • For repeated or scheduled announcements, cache generated audio files instead of re-synthesizing the same text every time.
  • Provide adjustable speed and volume options, since default TTS speech rates can be too fast or too slow depending on the listener.
  • Test pronunciation of technical terms or acronyms in advance — TTS engines often mispronounce abbreviations, error codes, or unusual words.

Security Considerations

  • Command injection through unsanitized input: If text originates from user input, logs, or external sources, avoid directly interpolating it into shell commands without quoting — always pass it as a quoted variable.
  • Unintended information disclosure: Be cautious about reading sensitive information (passwords, tokens, personal data) aloud in shared or recorded environments.
  • Untrusted cloud APIs: If using a cloud TTS provider, ensure API keys are stored securely (environment variables or a secrets manager) rather than hardcoded into scripts.
  • Audio file storage: Rendered speech files may contain sensitive spoken content — apply the same access controls you’d use for any other sensitive file.

Optimization Tips

  • Cache commonly repeated phrases (like “Build succeeded”) as pre-rendered .wav files instead of invoking the TTS engine every single time, saving CPU cycles on frequent notifications.
  • For batch conversion of many text files, process them in parallel using xargs -P to speed up rendering on multi-core systems:
ls *.txt | xargs -P 4 -I{} espeak-ng -f {} -w {}.wav
  • Lower the sample rate for non-critical notification audio to reduce file size when saving to .wav.

Troubleshooting Common Issues

Problem: espeak-ng: command not found. Install it via your package manager, or verify it’s in your PATH with which espeak-ng.

Problem: No sound is produced despite no errors. Check your system’s audio output device and volume levels; also confirm espeak-ng isn’t only writing to a file (-w flag) instead of playing audio directly.

Problem: Voice sounds robotic or hard to understand. Try a different voice with -v, adjust speed with -s, or consider using festival for a more natural-sounding alternative.

Problem: Script hangs when reading a large text file aloud. Break the file into smaller chunks (by paragraph or sentence) rather than passing the entire file as a single TTS call.

Common Mistakes to Avoid

  • Not checking whether a TTS engine is installed before calling it, resulting in confusing script failures.
  • Passing extremely long strings directly into a single TTS call instead of breaking them into digestible chunks.
  • Ignoring differences between platforms (Linux vs. macOS) and assuming espeak-ng or say is universally available.
  • Reading sensitive information aloud in a public or shared workspace without considering privacy implications.

Frequently Asked Questions

Do I need an internet connection for Bash text-to-speech? Not if you’re using offline engines like espeak-ng, espeak, or festival. Cloud-based options like Google Cloud TTS or Amazon Polly do require network access.

Can I change the language the TTS engine speaks in? Yes, most engines support multiple languages via a voice flag, e.g., espeak-ng -v fr "Bonjour tout le monde" for French.

How do I make the speech faster or slower? Use the -s flag with espeak-ng to set words-per-minute, or adjust the equivalent rate flag for whichever engine you’re using.

Can I use this to build an audiobook from a text file? Yes — render the text to a .wav file using the -f and -w flags, then optionally convert it to MP3 with ffmpeg for smaller file sizes.

Which engine sounds the most natural? Among free, offline options, festival tends to sound more natural than espeak-ng, though neither matches the quality of commercial cloud TTS services.

Summary

Bash doesn’t generate speech on its own, but it’s a fantastic glue layer for tools like espeak-ng, festival, or macOS’s say. Starting from a single line that speaks a string aloud, you can build a fully-featured TTS utility with adjustable speed, pitch, and voice, file-based rendering, and integration into notification pipelines for backups, builds, or accessibility needs. The real value comes from wiring TTS into automation you already run — turning silent scripts into ones that talk back.

References

  • eSpeak NG project documentation: https://github.com/espeak-ng/espeak-ng
  • Festival Speech Synthesis System: http://www.cstr.ed.ac.uk/projects/festival/
  • Bash Reference Manual: https://www.gnu.org/software/bash/manual/bash.html
  • GNU awk user guide: https://www.gnu.org/software/gawk/manual/gawk.html
Total
2
Shares

Leave a Reply

Previous Post
How to Create a Bash Image Resizer

How to Create a Bash Image Resizer

Next Post
How to Create a Bash Word Counter

How to Create a Bash Word Counter

Related Posts