SHA2560e0c3dd7ff312043e51aca170bde83c5d479bf1554ef2289b8943d2ae2f809bdVerify after downloading: echo "0e0c3dd7ff312043e51aca170bde83c5d479bf1554ef2289b8943d2ae2f809bd synonyme.sh" | sha256sum -c
Read it first, then run it. The complete source is further down on this page. There is deliberately no curl … | bash one-liner here.
Use at your own risk — no liability for data loss or system damage. See the terms of use.
What the script is for
You are writing, you get stuck on a word, and the search for a better one reliably leads to the browser — and from there into some tab that has nothing to do with the text any more. This script answers the question where you are already typing: in the terminal.
$ synonyme.sh schnell
zügig, rasch, flink, hurtig, geschwind, in Windeseile, im Eiltempo, fix, behände, …
The data comes from OpenThesaurus, a free German thesaurus. The script only reads, changes nothing and needs no account.
What it does
Asks OpenThesaurus for synonyms of a word (or a phrase)
Prints them comma-separated, wrapped to the width of your terminal
--plain prints one hit per line — so the result can be piped onwards (| fzf, | grep, | head)
--limit N shortens the list, --raw returns the JSON response untouched
The search word itself is dropped from the results, duplicates as well
How it works
It started as a shell function in my .bashrc that tore the XML interface apart with sed and cut. Cleaning it up for publication added three things one happily leaves out of a private function:
Encoding.wget "…?q=$*" breaks as soon as the word contains an umlaut or a space — and German words like doing that. With curl the library handles it:
No jq. The obvious move would be to filter the JSON response with jq — but jq is not installed on a fresh Debian, and a script that demands a package first does not get used a second time. For fields called "term", grep is entirely enough:
That awk '!seen[$0]++' is the shortest way to drop duplicates without destroying the order — sort -u would re-sort the hits alphabetically and throw away the ranking OpenThesaurus gives you.
Wrap only in a terminal. The original always wrapped at $(tput cols). In a pipe that is nonsense: there you want lines, not layout. So it only wraps when the output really lands in a terminal:
if [[ -t 1 ]] && command -v tput >/dev/null && COLS=$(tput cols 2>/dev/null); then
A script that looks different depending on whether it writes to a terminal or a pipe is not a bug — it is good Unix behaviour. ls does exactly the same thing.
Why I automated this
It was never an automation project, just a convenience: a function in .bashrc, aliased to syn. The reason to write it up as a script now is a different one — you cannot hand a .bashrc function to anyone. It has no --help, no error handling, and it breaks on the first word with an umlaut. That is exactly the difference between "works on my machine" and "somebody else can use this", and this small script is the shortest example of it I have.
A privacy note, because it belongs here: the search word is sent over HTTPS to openthesaurus.de. If you do not want that, do not use this script. The data there is licensed CC-BY-SA.
Usage
synonyme.sh Haus
synonyme.sh --limit 5 schnell
synonyme.sh --plain Datei | fzf # one hit per line
Source
synonyme.sh
#!/usr/bin/env bash
# ============================================================
# synonyme.sh — deutsche Synonyme im Terminal nachschlagen.
#
# Fragt OpenThesaurus (openthesaurus.de) und gibt die Treffer
# als Liste aus. Liest nur, ändert nichts am System.
#
# Hinweis: Das Suchwort wird an openthesaurus.de übertragen.
#
# Autor: Matthias Meister — https://scripts.web.mm-core.de
# Lizenz: MIT — Copyright (c) 2026 Matthias Meister
# Volltext: https://scripts.web.mm-core.de/lizenz/
# Version: 1.0.0
# ============================================================
set -euo pipefail
VERSION="1.0.0"
API="https://www.openthesaurus.de/synonyme/search"
TIMEOUT=10
LIMIT=0 # 0 = alle
PLAIN=0 # 1 = ein Synonym pro Zeile
RAW=0 # 1 = rohe JSON-Antwort ausgeben
usage() {
cat <<'EOF'
synonyme.sh — deutsche Synonyme nachschlagen (Quelle: openthesaurus.de).
Verwendung:
synonyme.sh WORT [WORT ...]
Optionen:
--limit N Nur die ersten N Treffer
--plain Ein Synonym pro Zeile (gut für Pipes und Skripte)
--raw Die rohe JSON-Antwort ausgeben
--version Version ausgeben
-h, --help Diese Hilfe
Beispiele:
synonyme.sh Haus
synonyme.sh --limit 5 schnell
synonyme.sh --plain Datei | fzf
Als Shell-Funktion (in ~/.bashrc):
syn() { /usr/local/bin/synonyme.sh "$@"; }
Exit-Code:
0 Treffer gefunden
1 kein Treffer oder Dienst nicht erreichbar
2 Aufruffehler
Datenschutz: Das Suchwort geht per HTTPS an openthesaurus.de.
Die Daten dort stehen unter CC-BY-SA — bei Weiterverwendung Quelle nennen.
EOF
}
die() { printf 'FEHLER: %s\n' "$1" >&2; exit 1; }
# ---- Argumente ----------------------------------------------
WORDS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--limit) LIMIT="${2:?--limit braucht eine Zahl}"; shift ;;
--plain) PLAIN=1 ;;
--raw) RAW=1 ;;
--version) printf 'synonyme.sh %s\n' "$VERSION"; exit 0 ;;
-h|--help) usage; exit 0 ;;
--) shift; WORDS+=("$@"); break ;;
-*) printf 'Unbekannte Option: %s\n\n' "$1" >&2; usage >&2; exit 2 ;;
*) WORDS+=("$1") ;;
esac
shift
done
[[ "${#WORDS[@]}" -gt 0 ]] || { usage >&2; exit 2; }
[[ "$LIMIT" =~ ^[0-9]+$ ]] || die "--limit braucht eine Zahl."
command -v curl >/dev/null || die "curl nicht gefunden (apt install curl)."
QUERY="${WORDS[*]}"
# ---- Abfrage -------------------------------------------------
# --data-urlencode übernimmt die Kodierung — wichtig für Umlaute und Leerzeichen.
RESPONSE=$(curl -fsSG --max-time "$TIMEOUT" \
--data-urlencode "q=${QUERY}" \
--data "format=application/json" \
"$API" 2>/dev/null) || die "OpenThesaurus nicht erreichbar."
if [[ "$RAW" -eq 1 ]]; then
printf '%s\n' "$RESPONSE"
exit 0
fi
# ---- Auswerten -----------------------------------------------
# Absichtlich ohne jq: das Paket ist auf einem frischen Debian nicht
# installiert, und für "term"-Felder reicht grep vollkommen.
mapfile -t TERMS < <(
printf '%s' "$RESPONSE" \
| grep -o '"term":"[^"]*"' \
| cut -d'"' -f4 \
| grep -vixF "$QUERY" \
| awk '!seen[$0]++'
)
if [[ "${#TERMS[@]}" -eq 0 ]]; then
printf 'Keine Synonyme gefunden für: %s\n' "$QUERY" >&2
exit 1
fi
if [[ "$LIMIT" -gt 0 && "$LIMIT" -lt "${#TERMS[@]}" ]]; then
TERMS=("${TERMS[@]:0:$LIMIT}")
fi
if [[ "$PLAIN" -eq 1 ]]; then
printf '%s\n' "${TERMS[@]}"
exit 0
fi
# Kommagetrennt, an die Terminalbreite umgebrochen — aber nur, wenn die
# Ausgabe wirklich in einem Terminal landet. In einer Pipe wäre der
# Umbruch nur störend.
LINE=$(printf '%s, ' "${TERMS[@]}")
LINE="${LINE%, }"
if [[ -t 1 ]] && command -v tput >/dev/null && COLS=$(tput cols 2>/dev/null) && [[ "$COLS" -gt 20 ]]; then
printf '%s\n' "$LINE" | fold -s -w "$COLS"
else
printf '%s\n' "$LINE"
fi