- gosh -c 'command' runs the command and exits, so gosh works as login shell for ssh host command, scp and rsync - gosh script runs a script file, lines starting with # are skipped - without a terminal, commands are read from stdin (byte by byte, so started commands can read the rest of stdin) - the exit status is the one of the last command (127 not found, 126 not executable, 128+n killed by signal n) - -i forces interactive mode, -l/--login is accepted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
154 lines
7.1 KiB
Markdown
154 lines
7.1 KiB
Markdown
`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 optional status code
|
||
quit [code] - Quit shell with optional status code
|
||
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
|
||
- 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. Da gosh `;`, `&&` und Pipes noch nicht kennt, darf der Befehl dabei aber nur aus einem einfachen Kommando bestehen.
|
||
|
||
### 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.
|