`gosh` eine Shell, in golang geschrieben. ``` gosh - Go Shell Version: 0.13.0 (branch: main, commit: f79fb0b) 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 source file - Run the commands of file in this shell (also: . file) jobs - List background and stopped jobs fg [%n] - Continue job n (default: current job) in the foreground bg [%n] - Continue stopped job n in the background wait [%n] - Wait for job n or all background jobs alias [name[=value] ...] - Define or show aliases (alias ll='ls -l') unalias name - Remove an alias (unalias -a: all aliases) : - Do nothing (exit status 0) break [n] - Leave the innermost (or n-th) for/while/until loop continue [n] - Continue with the next iteration of a loop 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 - Brace expansion: a{b,c}d -> abd acd, {1..5}, {01..10..2}, {a..e} - Wildcards: * ? [abc] [a-z] [!abc] match file names (not hidden files unless the pattern starts with a dot); quoted wildcards and values of variables are not expanded, a pattern without match is kept as is - Prompt user@host:dir $, with [N] before the $ if the last command failed with exit status N - Colored prompt (user@host green, red for root; directory blue; exit status red), configurable with $GOSH_COLORS, e.g. GOSH_COLORS='user=1;33:path=36' (keys user, root, path, error; 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 substitution: $(command) is replaced by the output of command (run in a subshell); unquoted it is split into words, "$(command)" stays one word - 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 - Control structures: if cmd; then ...; elif cmd; then ...; else ...; fi, for x in words; do ...; done, while cmd; do ...; done, until ..., ! cmd negates the status; they can span several lines (prompt "> ") - Pipes (a | b) and redirections: < file, > file, >> file, 2> file, 2>> file, 2>&1, >&2, &> file (stdout and stderr), &>> file - Job control: command & runs in the background, Ctrl+Z stops the foreground job; jobs, fg, bg and wait manage the jobs - Aliases replace the first word of a command in interactive shells (define them in ~/.goshrc); \name or 'name' skips the alias - Non-interactive use: gosh -c 'command', gosh script, command | gosh; the exit status is the one of the last command - Startup files: login shells (-l or argv[0] starting with -) read /etc/gosh/profile and ~/.gosh_profile, interactive shells read /etc/gosh/goshrc and ~/.goshrc (skip with --noprofile, --norc) ``` ### Aufruf ``` gosh [-l] [-i] [--norc] [--noprofile] [script] gosh [-l] [--noprofile] -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, liest die Profil-Dateien (siehe unten) | | `--norc` | `goshrc`-Dateien nicht lesen | | `--noprofile` | `profile`-Dateien nicht lesen | 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 und mit Pipes verbinden, z. B. `ssh host 'cd /srv && git log | head'`. ### Startdateien Beim Start liest gosh Startdateien und führt sie wie ein Skript in der laufenden Shell aus (Variablen, die dort gesetzt werden, bleiben also erhalten): | Shell | Dateien (in dieser Reihenfolge) | abschalten mit | |-------------------------------|----------------------------------------------|-----------------| | Login-Shell | `/etc/gosh/profile`, `~/.gosh_profile` | `--noprofile` | | interaktive Shell | `/etc/gosh/goshrc`, `~/.goshrc` | `--norc` | - Eine Login-Shell ist gosh mit `-l` oder wenn `argv[0]` mit `-` beginnt (so starten `login`, `sshd` und `su -` die Shell). Eine interaktive Login-Shell liest erst die Profil-, dann die `goshrc`-Dateien. - `gosh -c '…'` und Skripte lesen keine Startdateien, außer als Login-Shell (`gosh -l -c '…'`). - Fehlende Dateien werden übersprungen. Fehler werden mit Datei und Zeile gemeldet (`gosh: ~/.goshrc:4: foo: command not found`), die übrigen Zeilen laufen trotzdem. - Bash-Dateien wie `/etc/profile` oder `~/.bashrc` liest gosh bewusst nicht, weil es deren Syntax (`if`, Funktionen, …) nicht versteht. - Mit `source datei` (oder `. datei`) lässt sich eine Datei auch später einlesen, z. B. nach einer Änderung: `source ~/.goshrc`. Beispiel für `~/.goshrc`: ```bash # Eigene Programme zuerst export PATH=$HOME/bin:$HOME/go/bin:$PATH export EDITOR=vim # Aliase alias ll='ls -l' alias ..='cd ..' # Prompt-Farben (Anführungszeichen wegen des ;) export GOSH_COLORS='user=1;38;5;208:path=38;5;205' # Weitere Einstellungen nur, wenn die Datei existiert test -f ~/.goshrc.local && source ~/.goshrc.local ``` Beispiel für `~/.gosh_profile` (wird nur von Login-Shells gelesen, z. B. beim Anmelden an der Konsole oder per `ssh host` ohne Befehl): ```bash export LANG=de_DE.UTF-8 export PATH=$HOME/bin:$PATH ``` ### Kontrollstrukturen gosh kennt `if`, `for`, `while` und `until` wie `sh`, auf einer Zeile oder über mehrere Zeilen: ```bash if [ -f ~/.goshrc ]; then echo "rc vorhanden" elif [ -d ~/.config ]; then echo "nur .config" else echo "nichts" fi for f in *.log; do gzip "$f" done for i in {1..5}; do echo "Durchlauf $i"; done set n=3 while [ $n -gt 0 ]; do echo $n set n=$(expr $n - 1) done until ping -c1 -W1 server >/dev/null; do sleep 1; done ``` | Konstrukt | Bedeutung | |---------------------------------------------|-------------------------------------------------------------------| | `if befehl; then …; elif …; then …; else …; fi` | führt den Zweig aus, dessen Bedingung den Exit-Status 0 hat | | `for x in wörter; do …; done` | setzt `x` nacheinander auf jedes Wort | | `while befehl; do …; done` | wiederholt, solange `befehl` erfolgreich ist | | `until befehl; do …; done` | wiederholt, bis `befehl` erfolgreich ist | | `! befehl` | kehrt den Exit-Status um | | `break [n]`, `continue [n]` | verlässt die (n-te) Schleife bzw. springt zum nächsten Durchlauf | | `:` | tut nichts, Exit-Status 0 (z. B. `while :; do …; done`) | - Bedingungen sind normale Befehle (auch Listen mit `&&`/`||`), meist `test` bzw. `[ … ]` (z. B. `[ -f datei ]`, `[ "$a" = b ]`, `[ $n -lt 3 ]`). - Die Wörter nach `for … in` werden wie Argumente expandiert: `*.go`, `{1..10}`, `$VAR`, `$(befehl)`. Ohne `in` läuft die Schleife nicht (Positionsparameter gibt es nicht). - Variablen, `$(...)` und Wildcards im Rumpf werden bei jedem Durchlauf neu ersetzt. - Ist ein Befehl noch nicht vollständig (z. B. nach `if …; then` oder `for …` + Enter, nach `&&`, `|` oder einem `\` am Zeilenende), zeigt gosh den Fortsetzungs-Prompt `> `; Ctrl+C verwirft die angefangene Eingabe. Skripte und Startdateien lesen entsprechend weiter. - Ctrl+C bricht die ganze Befehlszeile ab, also auch eine Schleife (`while true; do sleep 1; done`). - Der Exit-Status ist der des zuletzt ausgeführten Befehls; ein `if` ohne passenden Zweig und eine Schleife ohne Durchlauf haben den Status 0. - Nicht unterstützt: Kontrollstrukturen in Pipes (`for …; done | sort`), mit Umleitung (`done > datei`) oder im Hintergrund (`done &`), außerdem `case`, Funktionen, `read` und Zuweisungen der Form `x=1` – Variablen setzt man mit `set x=1`, Arithmetik geht z. B. mit `$(expr $n + 1)`. ### Befehlsersetzung `$(befehl)` wird durch die Ausgabe von `befehl` ersetzt: ``` $ echo "Heute ist $(date +%A)" Heute ist Montag $ kill $(pgrep -f mein-server) $ cd $(git rev-parse --show-toplevel) $ vim $(grep -l TODO *.go) $ echo "Dateien: $(ls | wc -l)" ``` - Der Befehl läuft in einer eigenen Subshell (einem neuen gosh-Prozess): `cd`, `set` oder `export` darin ändern die laufende Shell nicht. Er darf alles enthalten, was gosh kann (Pipes, `;`, `&&`, Umleitungen, verschachtelte `$(...)`), und liest von der gleichen Eingabe, Fehlermeldungen gehen auf stderr. - Abschließende Zeilenumbrüche der Ausgabe werden entfernt. - Ohne Anführungszeichen wird die Ausgabe an Leerzeichen, Tabs und Zeilenumbrüchen in mehrere Wörter geteilt, `"$(befehl)"` bleibt ein einziges Wort (mit den Zeilenumbrüchen darin). - Anders als in `sh` werden Wildcards in der Ausgabe nicht expandiert, und Zeichen wie `;` oder `|` darin sind normaler Text: Die Ausgabe ist immer nur Daten. - Besteht ein Befehl nur aus einer Ersetzung (`$(false)`), ist ihr Exit-Status `$?`, sonst zählt der Status des Befehls. Ctrl+C während der Ersetzung bricht die ganze Zeile ab. - In `'...'` oder mit `\$(` wird nichts ersetzt. Die alte Schreibweise mit Backticks (`` `befehl` ``) und Arithmetik `$((1+2))` gibt es 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. ### Klammer-Expansion Wie in bash erzeugt `{…}` aus einem Wort mehrere Wörter: | Ausdruck | ergibt | |-------------------|---------------------------------| | `a{b,c}d` | `abd acd` | | `file{,.bak}` | `file file.bak` | | `{a,b}{1,2}` | `a1 a2 b1 b2` | | `x{a,{b,c}}` | `xa xb xc` (verschachtelt) | | `{1..5}` | `1 2 3 4 5` | | `{5..1}` | `5 4 3 2 1` | | `{1..10..3}` | `1 4 7 10` (Schrittweite) | | `{01..10..4}` | `01 05 09` (mit führenden Nullen) | | `{a..e}` | `a b c d e` | ``` $ mkdir -p projekt/{src,doc,test} $ cp config.yaml{,.bak} # Sicherungskopie $ ls *.{go,md} $ touch log-{01..12}.txt ``` - Die Klammer-Expansion kommt zuerst, die Ergebnisse durchlaufen danach `~`, Variablen und Wildcards: `*.{go,md}` wird zu `*.go *.md`, `{${A},b}` zu `$A b`. - Ohne Komma (und ohne gültige Folge) bleiben Klammern stehen: `{a}`, `{}`, `{a..1}`. Auch `${VAR}` wird nicht angefasst. - In Anführungszeichen oder mit `\` davor sind die Zeichen normale Zeichen: `"{a,b}"`, `\{a,b\}`. Ein Komma lässt sich mit `\,` schützen: `{a\,b,c}` ergibt `a,b c`. - Bei einer Umleitung muss genau ein Wort herauskommen, `> {a,b}` meldet `ambiguous redirect`. - Ein Wort darf höchstens 100 000 Wörter erzeugen. ### Wildcards Wörter mit `*`, `?` oder `[...]` werden durch die passenden Dateinamen ersetzt, sortiert: | Muster | passt auf | |-----------|-----------------------------------------------------| | `*` | beliebig viele Zeichen (auch keine) | | `?` | genau ein Zeichen | | `[abc]` | eines der Zeichen `a`, `b`, `c` | | `[a-z]` | ein Zeichen aus dem Bereich | | `[!abc]` | ein Zeichen außer `a`, `b`, `c` (auch `[^abc]`) | ``` $ ls *.go main.go main_test.go $ cat logs/2026-*/error.log $ ls -d */ # nur Verzeichnisse ``` - Muster gelten pro Pfadteil, `*` passt nie auf `/`: `*/*.go` findet `.go`-Dateien eine Ebene tiefer. - Versteckte Dateien (mit `.` am Anfang) werden nur gefunden, wenn das Muster selbst mit `.` beginnt: `*` findet `.bashrc` nicht, `.*rc` schon. - Passt keine Datei, bleibt das Wort unverändert stehen (wie in `sh`), `ls *.xyz` meldet dann also `ls` den Fehler. - In Anführungszeichen oder mit `\` davor sind die Zeichen normale Zeichen: `echo "*"`, `echo '*.go'`, `echo \*`. - Werte von Variablen werden nicht als Muster behandelt (anders als in `sh`): Nach `set P=*.go` gibt `echo $P` wörtlich `*.go` aus. So kann ein Variablenwert nie unbemerkt auf viele Dateien passen. `echo $DIR/*.go` funktioniert dagegen, weil das `*` direkt in der Zeile steht. - Bei einer Umleitung muss das Muster genau eine Datei treffen (`> *.log`), sonst meldet gosh `ambiguous redirect`. ### Pipes und Umleitungen ``` $ ls | sort -r | head -3 $ make > build.log 2>&1 $ grep -c error < build.log ``` | Syntax | Bedeutung | |-----------------|------------------------------------------------------------| | `a \| b` | stdout von `a` wird stdin von `b` | | `< datei` | stdin aus `datei` lesen | | `> datei` | stdout in `datei` schreiben (wird überschrieben) | | `>> datei` | stdout an `datei` anhängen | | `2> datei` | stderr in `datei` schreiben (`2>>` hängt an) | | `2>&1` | stderr dorthin, wohin stdout gerade geht | | `>&2` | stdout dorthin, wohin stderr gerade geht | | `&> datei` | stdout und stderr in `datei` (`&>>` hängt an) | | `> datei` | ohne Befehl: legt `datei` an bzw. leert sie | - Umleitungen werden von links nach rechts ausgewertet: `befehl > log 2>&1` schreibt beides in `log`, `befehl 2>&1 > log` schickt stderr dorthin, wo stdout vorher hinging (z. B. in eine Pipe). - Unterstützt werden die Dateideskriptoren 0, 1 und 2. Vor `>` und `<` zählt nur eine Zahl ohne Anführungszeichen als Deskriptor (`2>f`, aber nicht `x2>f` oder `"2">f`). - Operatoren in Anführungszeichen oder mit `\` davor sind normale Zeichen: `echo "a|b" 'c>d' e\>f`. - Der Exit-Status einer Pipeline ist der des letzten Befehls. Beendet sich ein Befehl am Ende (z. B. `head`), bekommt der schreibende Befehl davor `SIGPIPE`, `yes | head -1` endet also sofort. - Die Built-ins `pwd` und `help` lassen sich umleiten und in Pipes verwenden (`help | less`). `cd`, `set`, `export`, `exit` und `quit` verändern die Shell selbst und sind in einer Pipeline nicht erlaubt. ### Aliase Ein Alias ist ein Kurzname für einen Befehl, am besten in `~/.goshrc` definiert: ```bash alias ll='ls -l' alias la='ls -la' alias ..='cd ..' alias gs='git status' alias ls='ls --color=auto' # ein Alias darf den eigenen Namen enthalten alias please='sudo ' # endet mit Leerzeichen: das nächste Wort wird auch ersetzt ``` | Befehl | Bedeutung | |-------------------------|---------------------------------------------------| | `alias name='wert'` | definiert einen Alias (mehrere in einem Aufruf möglich) | | `alias` | listet alle Aliase (in einer Form, die sich wieder einlesen lässt) | | `alias name` | zeigt einen Alias | | `unalias name` | entfernt einen Alias, `unalias -a` alle | - Ersetzt wird nur das erste Wort eines Befehls, also am Zeilenanfang und nach `;`, `&&`, `||`, `|` und `&`: `ll | less` funktioniert, `echo ll` gibt `ll` aus. - Der Wert darf selbst mehrere Befehle enthalten (`alias up='cd .. && ls'`) und wird wiederum auf Aliase geprüft. Ein Alias, der gerade ersetzt wird, wird darin nicht noch einmal ersetzt, deshalb funktioniert `alias ls='ls --color=auto'`. - Endet der Wert mit einem Leerzeichen, wird auch das nächste Wort als Alias geprüft: Mit den Aliasen oben wird `please ll` zu `sudo ls --color=auto -l`. - Mit `\ll` oder `'ll'` umgeht man den Alias und ruft das Programm direkt auf. - Aliase gelten nur in interaktiven Shells (wie in bash), also auch in `~/.goshrc`, aber nicht in Skripten und bei `gosh -c`. Ein Alias wirkt erst ab der nächsten Zeile, nicht in der Zeile, in der er definiert wird. - Erlaubt sind Namen aus Buchstaben, Ziffern und `_ - . + : , @ % ^`. - Aliase werden auch mit Tab vervollständigt. ### Job-Control In einer interaktiven Shell läuft jede Befehlszeile (bzw. jede Pipeline) als eigener *Job* in einer eigenen Prozessgruppe: ``` $ sleep 100 & # im Hintergrund starten [1] 12345 $ vim notizen.txt # Ctrl+Z hält vim an ^Z [2]+ Stopped vim notizen.txt $ jobs [1]- Running sleep 100 & [2]+ Stopped vim notizen.txt $ fg # vim wieder im Vordergrund $ bg %2 # oder: angehaltenen Job im Hintergrund weiterlaufen lassen ``` | Befehl / Taste | Bedeutung | |----------------|---------------------------------------------------------------------------| | `befehl &` | startet den Befehl im Hintergrund, gosh wartet nicht auf ihn (`$?` ist 0) | | Ctrl+Z | hält den Vordergrund-Job an (`$?` wird 128+SIGTSTP, unter Linux 148) | | Ctrl+C | bricht nur den Vordergrund-Job ab, nicht die Shell | | `jobs` | listet laufende und angehaltene Jobs | | `fg [%n]` | holt Job `n` in den Vordergrund (und setzt ihn fort, falls angehalten) | | `bg [%n]` | setzt einen angehaltenen Job im Hintergrund fort | | `wait [%n]` | wartet auf Job `n` bzw. auf alle Hintergrund-Jobs | - Ohne Angabe gilt der *aktuelle* Job (in `jobs` mit `+` markiert, der zuletzt angehaltene oder in den Hintergrund geschickte), `%-` ist der vorherige (`-`). `%n` und `n` meinen Job Nummer `n`. - Beendete Hintergrund-Jobs meldet gosh vor dem nächsten Prompt (`[1]+ Done sleep 100`, bei Fehlern `Exit N`). - Liest ein Hintergrund-Job vom Terminal, wird er angehalten (wie in bash), mit `fg` bekommt er die Eingabe. - Die Terminal-Einstellungen werden gesichert und wiederhergestellt: Nach dem Anhalten von `vim` oder `less` funktioniert der Prompt normal, nach `fg` bekommt das Programm seine Einstellungen zurück. - `exit` (oder Ctrl+D) mit angehaltenen Jobs warnt einmal (`there are stopped jobs`), ein zweites `exit` direkt danach beendet gosh. - `cd`, `set`, `export`, `exit`, `source`, `fg`, `bg` und `wait` lassen sich nicht mit `&` im Hintergrund starten. - In Skripten und mit `gosh -c` gibt es keine Job-Control (kein Ctrl+Z, kein `fg`/`bg`), `&` und `wait` funktionieren aber. Hintergrund-Jobs lesen dort von `/dev/null`. - Wird ein angehaltener Job von außen fortgesetzt (`kill -CONT`), zeigt `jobs` ihn weiter als `Stopped`, bis er endet oder erneut angehalten wird; `fg` und `bg` funktionieren trotzdem. - Einen Job beenden lässt sich mit `kill` über die Prozess-ID (z. B. `kill 12345`); `kill %1` versteht gosh noch nicht. ### Prompt Der Prompt zeigt Benutzer, Rechner und Verzeichnis. Ist der letzte Befehl fehlgeschlagen, steht sein Exit-Status in eckigen Klammern davor, bis wieder ein Befehl erfolgreich war: ``` flow@host:~/src $ false flow@host:~/src [1] $ gibtsnicht gosh: gibtsnicht: command not found flow@host:~/src [127] $ sleep 100 # mit Ctrl+C abgebrochen ^C flow@host:~/src [130] $ true flow@host:~/src $ ``` Typische Werte: `1` allgemeiner Fehler, `2` Syntaxfehler, `126` nicht ausführbar, `127` Befehl nicht gefunden, `128+n` durch Signal `n` beendet (`130` = Ctrl+C, `148` = mit Ctrl+Z angehalten unter Linux). Eine leere Eingabe ändert den Status nicht. ### 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) | | `error` | Exit-Status `[N]` nach einem Fehler | `1;31` (fett rot) | 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' # Fehler-Status gelb auf rotem Hintergrund GOSH_COLORS='error=1;33;41' # 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.