Files
gosh/README.md
T
Florian WaltherandClaude Opus 5.5 f5c14dff90 add command lists (;, &&, ||) and $?
- split input at unquoted ;, && and || and run the commands like sh;
  variables are expanded per command when it runs
- $? and ${?} expand to the exit status of the last command,
  exit/quit without argument use it
- syntax errors for missing commands around operators (status 2)
- "command not found" message instead of the raw exec error, no more
  "exit status N" message when a command fails

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 20:32:39 +02:00

180 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`gosh` eine einfache Shell, geschrieben in golang.
```
gosh - Go Shell
Version: 0.2.1 (branch: main, commit: 03e791c)
Built-in commands:
cd [dir] - Change directory (cd with no args goes to home)
pwd - Print working directory
echo [args] - Echo arguments
exit [code] - Exit shell with status code (default: $?)
quit [code] - Quit shell with status code (default: $?)
set VAR=val - Set environment variable
export VAR=val - Export environment variable
help - Show this help
Features:
- Argument parsing with quotes and escapes ('...' is literal,
"..." expands variables, \ escapes the next character)
- Ctrl+D to exit, Ctrl+C to cancel the current line or command
- Command history (up/down arrows), stored in
$XDG_STATE_HOME/gosh/history (default ~/.local/state/gosh/history,
override with $GOSH_HISTFILE); lines starting with a space are not saved
- Tab completion for commands and file paths
- Tilde expansion (~, ~/path) at the start of unquoted words
- Colored prompt (user@host green, red for root; directory blue),
configurable with $GOSH_COLORS, e.g. GOSH_COLORS='user=1;33:path=36'
(keys user, root, path; values are ANSI SGR codes, empty = no color);
disabled with $NO_COLOR, TERM=dumb or when output is not a terminal
- Environment variable expansion ($VAR, ${VAR}, ${VAR:-default});
using an unset variable is an error, use ${VAR:-} for an empty default
- Command lists: a ; b (run both), a && b (b only if a succeeded),
a || b (b only if a failed); $? is the exit status of the last command
- Non-interactive use: gosh -c 'command', gosh script, command | gosh;
the exit status is the one of the last command
```
### Aufruf
```
gosh [-l] [-i] [script]
gosh [-l] -c command
gosh -version
```
| Aufruf | Verhalten |
|------------------------|------------------------------------------------------------------|
| `gosh` | interaktive Shell (wenn stdin ein Terminal ist) |
| `gosh -c 'befehl'` | führt den Befehl aus und beendet sich |
| `gosh skript.gosh` | führt das Skript Zeile für Zeile aus |
| `befehle \| gosh` | liest die Befehle von stdin |
| `-i` | erzwingt den interaktiven Modus, auch ohne Terminal |
| `-l`, `--login` | Login-Shell (wird akzeptiert, eine Startdatei gibt es noch nicht) |
Ohne Terminal bricht gosh bei einem fehlgeschlagenen Befehl nicht ab und beendet sich mit dem Exit-Status des letzten Befehls: `127`, wenn ein Befehl nicht gefunden wurde, `126`, wenn er nicht ausführbar ist, `128+n`, wenn er durch Signal `n` beendet wurde. Leere Zeilen und Zeilen, die mit `#` beginnen, werden übersprungen, Skripte können also mit `#!/usr/bin/gosh` beginnen.
Damit funktionieren auch `ssh host befehl`, `scp` und `rsync`, wenn gosh als Login-Shell gesetzt ist. Befehle lassen sich mit `;`, `&&` und `||` verketten (z. B. `ssh host 'cd /srv && git pull'`), Pipes und Umleitungen kennt gosh aber noch nicht.
### Befehlslisten und Exit-Status
Mehrere Befehle in einer Zeile werden wie in `sh` verknüpft:
| Liste | Bedeutung |
|------------|---------------------------------------------------|
| `a ; b` | erst `a`, dann `b` |
| `a && b` | `b` nur, wenn `a` erfolgreich war (Exit-Status 0) |
| `a \|\| b` | `b` nur, wenn `a` fehlgeschlagen ist |
`&&` und `||` werden von links nach rechts ausgewertet, `a && b || c` führt also `c` aus, wenn `a` oder `b` fehlschlägt. Variablen werden erst beim Ausführen des jeweiligen Befehls expandiert, `cd /tmp && echo $PWD` gibt also schon das neue Verzeichnis aus.
`$?` (bzw. `${?}`) enthält den Exit-Status des letzten Befehls, `exit` ohne Argument beendet gosh mit diesem Status:
```
$ false; echo $?
1
$ gibtsnicht || echo "Status $?"
gosh: gibtsnicht: command not found
Status 127
```
Schlägt ein Programm fehl, gibt gosh selbst keine Meldung mehr aus (das Programm meldet den Fehler selbst), nur bei Fehlern von gosh, z. B. wenn ein Befehl nicht gefunden wird.
### Farben
Der Prompt wird über die Umgebungsvariable `GOSH_COLORS` eingefärbt. Sie besteht aus durch `:` getrennten Einträgen `schlüssel=code`:
| Schlüssel | Teil des Prompts | Standard |
|-----------|--------------------------------|---------------------|
| `user` | `user@host` | `1;32` (fett grün) |
| `root` | `user@host`, wenn man root ist | `1;31` (fett rot) |
| `path` | aktuelles Verzeichnis | `1;34` (fett blau) |
Die Werte sind [ANSI-SGR-Codes](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters), mehrere werden mit `;` kombiniert:
- `1` fett, `2` blass, `3` kursiv, `4` unterstrichen
- `30`–`37` Vordergrund, `90`–`97` helle Vordergrundfarben, `40`–`47` Hintergrund
- `38;5;N` eine der 256 Farben, `38;2;R;G;B` Truecolor (sofern das Terminal es kann)
Nicht angegebene Schlüssel behalten ihre Standardfarbe, ein leerer Wert (`path=`) schaltet die Farbe für diesen Teil ab. Ungültige Werte werden ignoriert. Da nur Ziffern und `;` erlaubt sind, lassen sich über die Variable keine beliebigen Escape-Sequenzen ins Terminal schreiben. `NO_COLOR` schaltet alle Farben ab.
Gesetzt wird die Variable z. B. in `~/.profile` oder direkt in gosh (wirkt ab dem nächsten Prompt):
```bash
export GOSH_COLORS='user=1;33:path=1;36' # in ~/.profile
set 'GOSH_COLORS=user=1;33:path=1;36' # in gosh
```
Ein paar Kombinationen zum Ausprobieren:
```bash
# Solarized: gelb und cyan
GOSH_COLORS='user=33:path=36'
# Sonnenuntergang: orange und pink (256 Farben)
GOSH_COLORS='user=1;38;5;208:path=38;5;205'
# Matrix: hellgrün und blassgrün
GOSH_COLORS='user=1;92:path=2;32'
# Dracula: lila und pink (Truecolor)
GOSH_COLORS='user=1;38;2;189;147;249:path=38;2;255;121;198'
# Nord: frostblau und hellgrau (Truecolor)
GOSH_COLORS='user=1;38;2;136;192;208:path=38;2;216;222;233'
# Ozean: türkis und kursiv blau
GOSH_COLORS='user=1;96:path=3;94'
# Root-Warnung: weiß auf rotem Hintergrund
GOSH_COLORS='root=1;97;41'
# Dezent: nur das Verzeichnis unterstrichen
GOSH_COLORS='user=:path=4'
```
### Sicherheitshinweise
- Die History liegt in `$XDG_STATE_HOME/gosh/history` (Standard: `~/.local/state/gosh/history`) und ist nur für den eigenen Benutzer lesbar (Verzeichnis `0700`, Datei `0600`). Mit `GOSH_HISTFILE` lässt sich ein anderer Pfad setzen, mit `GOSH_HISTFILE=` wird die History abgeschaltet.
- Zeilen, die mit einem Leerzeichen beginnen, werden nicht in der History gespeichert – praktisch für Befehle mit Passwörtern oder Tokens.
- Innerhalb von `'...'` wird nichts expandiert, `\$` ergibt ein wörtliches `$`.
- Eine nicht gesetzte Variable bricht den Befehl ab (wie `set -u`), damit z. B. `rm -rf $DIR/out` nie mit leerem `$DIR` ausgeführt wird. Für einen Standardwert `${VAR:-default}` bzw. `${VAR:-}` verwenden.
- Variablenwerte werden nicht in mehrere Wörter aufgeteilt.
- Steuerzeichen in Verzeichnisnamen werden im Prompt escaped angezeigt (z. B. `\x1b`), Dateien mit Steuerzeichen im Namen werden nicht vervollständigt.
- Ctrl+C beendet nur den laufenden Befehl bzw. verwirft die aktuelle Zeile, nicht die Shell.
_gosh is not meant for production use, it is just a proof of concept._
## building
```bash
go build ./
```
Die Version lässt sich mit `gosh -version` (oder `--version`, `-V`) abfragen und steht auch in der Ausgabe von `help`:
```
$ gosh -version
gosh 0.2.1 (branch: main, commit: 03e791c)
```
Version und Branch werden beim Bauen mit GoReleaser per `-ldflags` gesetzt. Bei einem einfachen `go build` steht dort `dev`, der Commit wird dann aus den von Go eingebetteten Git-Informationen gelesen (mit `-dirty`, falls es uncommittete Änderungen gab).
## Release
Binaries sowie `.deb`- und `.rpm`-Pakete werden mit [GoReleaser](https://goreleaser.com) gebaut (Konfiguration: `.goreleaser.yaml`).
```bash
# lokal testen, ohne etwas zu veröffentlichen (Ergebnis in dist/)
goreleaser release --snapshot --clean
# Release auf Gitea veröffentlichen
export GITEA_TOKEN=...
git tag -a v0.1.0 -m "v0.1.0"
git push origin v0.1.0
goreleaser release --clean
```
Das Paket installiert `/usr/bin/gosh` und trägt die Shell in `/etc/shells` ein, sodass sie mit `chsh -s /usr/bin/gosh` gesetzt werden kann.