Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
unset VAR - Remove a 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
- Variables: name=value sets a variable (like set), name=value command
sets it only for that command; the value is not split or expanded as
a pattern
- 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
-loder wennargv[0]mit-beginnt (so startenlogin,sshdundsu -die Shell). Eine interaktive Login-Shell liest erst die Profil-, dann diegoshrc-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/profileoder~/.bashrcliest 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:
# 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):
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:
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
n=3
while [ $n -gt 0 ]; do
echo $n
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
&&/||), meisttestbzw.[ … ](z. B.[ -f datei ],[ "$a" = b ],[ $n -lt 3 ]). - Die Wörter nach
for … inwerden wie Argumente expandiert:*.go,{1..10},$VAR,$(befehl). Ohneinlä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 …; thenoderfor …+ 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
ifohne 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ßerdemcase, Funktionen undread. Arithmetik geht z. B. mitn=$(expr $n + 1).
Variablen
name=wert # Variable setzen (wie set name=wert)
a=1 b=2 # mehrere auf einmal
gruss="Hallo Welt" # Leerzeichen im Wert brauchen Anführungszeichen
heute=$(date +%F) # Ausgabe eines Befehls
PATH=~/bin:$PATH # ~ wird auch nach = und : ersetzt
LANG=C sort datei # nur für diesen einen Befehl
unset name # Variable entfernen
echo $name ${name:-standard}
- Eine Zuweisung ist ein Wort
name=wertam Anfang eines Befehls, bei demnameund=ohne Anführungszeichen stehen undnameaus Buchstaben, Ziffern und_besteht (nicht mit einer Ziffer beginnend)."x=1",x\=1undecho x=1sind keine Zuweisungen. - Steht danach kein Befehl, wird die Variable in der Shell gesetzt. Wie bei
setundexportsind in gosh alle Variablen auch für gestartete Programme sichtbar (es gibt keine reinen Shell-Variablen). - Steht ein Befehl danach (
LANG=C sort datei), gilt die Zuweisung nur für diesen Befehl, die Shell behält den alten Wert. - Der Wert wird nicht in Wörter geteilt und nicht als Wildcard oder
{a,b}expandiert:x=$(ls)speichert die ganze Ausgabe,x=*.gospeichert*.go. Variablen,$(...)und~(am Anfang, nach=und nach:) werden ersetzt. $?ist nach einer Zuweisung der Status des letzten$(...)darin, sonst 0.- Variablen in den Argumenten werden ersetzt, bevor die Zuweisung davor gilt:
x=1 echo $xgibt nicht1aus, sondern meldetx: unbound variable(wennxvorher nicht gesetzt war). - In einer Pipeline (
x=1 | cat) ändert eine Zuweisung die Shell nicht.
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,setoderexportdarin ä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
shwerden 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}ergibta,b c. - Bei einer Umleitung muss genau ein Wort herauskommen,
> {a,b}meldetambiguous 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/:*/*.gofindet.go-Dateien eine Ebene tiefer. - Versteckte Dateien (mit
.am Anfang) werden nur gefunden, wenn das Muster selbst mit.beginnt:*findet.bashrcnicht,.*rcschon. - Passt keine Datei, bleibt das Wort unverändert stehen (wie in
sh),ls *.xyzmeldet dann alsolsden 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): NachP='*.go'gibtecho $Pwörtlich*.goaus. So kann ein Variablenwert nie unbemerkt auf viele Dateien passen.echo $DIR/*.gofunktioniert dagegen, weil das*direkt in der Zeile steht. - Bei einer Umleitung muss das Muster genau eine Datei treffen (
> *.log), sonst meldet goshambiguous 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>&1schreibt beides inlog,befehl 2>&1 > logschickt 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 nichtx2>foder"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 davorSIGPIPE,yes | head -1endet also sofort. - Die Built-ins
pwdundhelplassen sich umleiten und in Pipes verwenden (help | less).cd,set,export,exitundquitverä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:
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 | lessfunktioniert,echo llgibtllaus. - 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 funktioniertalias 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 llzusudo ls --color=auto -l. - Mit
\lloder'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 beigosh -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
jobsmit+markiert, der zuletzt angehaltene oder in den Hintergrund geschickte),%-ist der vorherige (-).%nundnmeinen Job Nummern. - Beendete Hintergrund-Jobs meldet gosh vor dem nächsten Prompt (
[1]+ Done sleep 100, bei FehlernExit N). - Liest ein Hintergrund-Job vom Terminal, wird er angehalten (wie in bash), mit
fgbekommt er die Eingabe. - Die Terminal-Einstellungen werden gesichert und wiederhergestellt: Nach dem Anhalten von
vimoderlessfunktioniert der Prompt normal, nachfgbekommt das Programm seine Einstellungen zurück. exit(oder Ctrl+D) mit angehaltenen Jobs warnt einmal (there are stopped jobs), ein zweitesexitdirekt danach beendet gosh.cd,set,export,exit,source,fg,bgundwaitlassen sich nicht mit&im Hintergrund starten.- In Skripten und mit
gosh -cgibt es keine Job-Control (kein Ctrl+Z, keinfg/bg),&undwaitfunktionieren aber. Hintergrund-Jobs lesen dort von/dev/null. - Wird ein angehaltener Job von außen fortgesetzt (
kill -CONT), zeigtjobsihn weiter alsStopped, bis er endet oder erneut angehalten wird;fgundbgfunktionieren trotzdem. - Einen Job beenden lässt sich mit
killüber die Prozess-ID (z. B.kill 12345);kill %1versteht 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, mehrere werden mit ; kombiniert:
1fett,2blass,3kursiv,4unterstrichen30–37Vordergrund,90–97helle Vordergrundfarben,40–47Hintergrund38;5;Neine der 256 Farben,38;2;R;G;BTruecolor (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):
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:
# 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 (Verzeichnis0700, Datei0600). MitGOSH_HISTFILElässt sich ein anderer Pfad setzen, mitGOSH_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/outnie mit leerem$DIRausgefü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
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 gebaut (Konfiguration: .goreleaser.yaml).
# 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 die Man-Page gosh(1) (man gosh, Quelle: packaging/man/gosh.1, lokal ansehen mit man ./packaging/man/gosh.1) und trägt die Shell in /etc/shells ein, sodass sie mit chsh -s /usr/bin/gosh gesetzt werden kann.