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 thanespeak.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>&1checks whether thesaycommand exists on the system without printing anything to the terminal, redirecting both stdout and stderr to/dev/null.- The
if/elifchain tries macOS’ssayfirst, then falls back toespeak-ng, then plainespeak, 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"
-scontrols speaking rate in words per minute (default is around 175).-pcontrols 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"tellsespeak-ngto 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
.wavfiles 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 -Pto 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-ngorsayis 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
awkuser guide: https://www.gnu.org/software/gawk/manual/gawk.html
