diff --git a/.gitignore b/.gitignore index 60e4762..4c7c8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ gosh dist/ +packaging/man/gosh.1.gz diff --git a/.goreleaser.yaml b/.goreleaser.yaml index b17f6d8..e7d2940 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -12,6 +12,8 @@ before: hooks: - go mod tidy - go test ./... + # Man page with the release version, gzipped for /usr/share/man + - sh -c 'sed "s/@VERSION@/{{ .Version }}/" packaging/man/gosh.1 | gzip -9n > packaging/man/gosh.1.gz' builds: - id: gosh @@ -50,6 +52,10 @@ archives: - src: README.md info: mode: 0644 + - src: packaging/man/gosh.1.gz + dst: man/gosh.1.gz + info: + mode: 0644 nfpms: - id: gosh @@ -75,6 +81,10 @@ nfpms: dst: /usr/share/doc/gosh/README.md file_info: mode: 0644 + - src: packaging/man/gosh.1.gz + dst: /usr/share/man/man1/gosh.1.gz + file_info: + mode: 0644 scripts: postinstall: packaging/scripts/postinstall.sh postremove: packaging/scripts/postremove.sh diff --git a/README.md b/README.md index 445ae0c..276f110 100644 --- a/README.md +++ b/README.md @@ -507,4 +507,4 @@ 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. +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. diff --git a/packaging/man/gosh.1 b/packaging/man/gosh.1 new file mode 100644 index 0000000..f34c336 --- /dev/null +++ b/packaging/man/gosh.1 @@ -0,0 +1,504 @@ +.\" Man page for gosh. View with: man ./packaging/man/gosh.1 +.\" @VERSION@ is replaced by the release version when building packages. +.TH GOSH 1 "October 2026" "gosh @VERSION@" "User Commands" +.SH NAME +gosh \- a simple shell written in Go +.SH SYNOPSIS +.B gosh +.RB [ \-l ] +.RB [ \-i ] +.RB [ \-\-norc ] +.RB [ \-\-noprofile ] +.RI [ script ] +.br +.B gosh +.RB [ \-l ] +.RB [ \-\-noprofile ] +.B \-c +.I command +.br +.B gosh +.B \-version +.SH DESCRIPTION +.B gosh +is a small command interpreter with a syntax close to +.BR sh (1). +It supports command history, tab completion, variables, command +substitution, command lists, control structures, pipes, redirections, +wildcards, brace expansion, aliases and job control. +.PP +Without arguments and with a terminal on standard input, +.B gosh +starts an interactive shell. With a +.I script +argument it runs the commands of that file, with +.B \-c +the given command string, and otherwise it reads commands from standard +input. +.PP +.B gosh +is a proof of concept and not meant for production use. +.SH OPTIONS +.TP +.B \-c \fIcommand\fP +Run +.I command +and exit. +.TP +.B \-i +Force interactive mode, even if standard input is not a terminal. +.TP +.BR \-l ", " \-\-login +Act as a login shell and read the profile files (see +.BR "STARTUP FILES" ). +A shell whose +.I argv[0] +starts with +.B \- +(as started by +.BR login (1), +.BR sshd (8) +or +.BR "su \-" ) +is a login shell as well. +.TP +.B \-\-norc +Do not read the goshrc files. +.TP +.B \-\-noprofile +Do not read the profile files. +.TP +.BR \-version ", " \-\-version ", " \-V +Print the version, branch and commit and exit. +.TP +.BR \-h ", " \-\-help +Print a usage summary and exit. +.TP +.B \- +Read commands from standard input. +.SH NON-INTERACTIVE USE +Scripts, command strings and commands read from a pipe are executed line +by line. Empty lines and lines starting with +.B # +are skipped, so a script may start with +.BR "#!/usr/bin/gosh" . +A failing command does not abort the script; the exit status of +.B gosh +is the one of the last command. +.PP +Because +.B gosh +supports +.BR \-c , +.BR ssh (1), +.BR scp (1) +and +.BR rsync (1) +work when it is used as login shell. +.SH STARTUP FILES +Startup files are executed in the running shell, so variables and +aliases defined there stay in effect. +.TP +Login shells +read +.I /etc/gosh/profile +and +.IR ~/.gosh_profile . +.TP +Interactive shells +read +.I /etc/gosh/goshrc +and +.IR ~/.goshrc . +.PP +An interactive login shell reads the profile files first, then the goshrc +files. Scripts and +.B \-c +read no startup files unless +.B gosh +is a login shell. Missing files are skipped; errors are reported with file +name and line number and the remaining lines still run. Files of other +shells such as +.I /etc/profile +or +.I ~/.bashrc +are not read. +.SH SYNTAX +.SS Quoting +.TP +.B \(aq...\(aq +Everything is literal. +.TP +.B \(dq...\(dq +Variables and command substitutions are expanded, the result stays one +word. +.TP +.B \e +Escapes the next character. +.SS Variables +.TP +.I name\fB=\fPvalue +Set a variable. All variables are exported to the environment of started +programs. The value is not split into words and not expanded as a pattern; +variables, command substitutions and +.B ~ +(at the start, after +.B = +and after +.BR : ) +are expanded. +.TP +.I name\fB=\fPvalue command +Set the variable only for +.IR command . +.TP +.BR $VAR ", " ${VAR} +The value of +.BR VAR . +Using an unset variable is an error and aborts the command. +.TP +.B ${VAR:\-default} +The value of +.BR VAR , +or +.I default +if it is unset or empty. Use +.B ${VAR:\-} +for an empty default. +.TP +.B $? +The exit status of the last command. +.SS Command substitution +.B $(\fIcommand\fP) +is replaced by the output of +.IR command , +which runs in a subshell. Trailing newlines are removed. Unquoted, the +output is split into words at blanks and newlines; inside double quotes it +stays one word. Wildcards and operators in the output are not interpreted. +Backticks and arithmetic expansion are not supported. +.SS Tilde and brace expansion +.B ~ +and +.B ~/path +at the start of an unquoted word are replaced by the home directory. +.PP +Brace expansion generates several words from one: +.B a{b,c}d +becomes +.BR "abd acd" , +.B {1..5} +becomes +.BR "1 2 3 4 5" , +.B {01..10..4} +becomes +.B 01 05 09 +and +.B {a..e} +becomes +.BR "a b c d e" . +Brace expansion happens before all other expansions. +.SS Wildcards +.B * +matches any string, +.B ? +any single character, +.B [abc] +and +.B [a\-z] +one of the given characters and +.B [!abc] +(or +.BR [^abc] ) +any character except those. Matches are sorted. Hidden files are only +matched if the pattern starts with a dot. A pattern without match is kept +unchanged. Quoted wildcards and values of variables are never expanded. +.SS Command lists +.TP +.IB a " ; " b +Run +.IR a , +then +.IR b . +.TP +.IB a " && " b +Run +.I b +only if +.I a +succeeded. +.TP +.IB a " || " b +Run +.I b +only if +.I a +failed. +.TP +.IB command " &" +Run +.I command +in the background. +.SS Pipes and redirections +.TP +.IB a " | " b +Connect standard output of +.I a +to standard input of +.IR b . +The exit status is the one of +.IR b . +.TP +.BI < " file" +Read standard input from +.IR file . +.TP +.BR > " \fIfile\fP, " >> " \fIfile\fP" +Write (or append) standard output to +.IR file . +.TP +.BR 2> " \fIfile\fP, " 2>> " \fIfile\fP" +Write (or append) standard error to +.IR file . +.TP +.B 2>&1 +Redirect standard error to where standard output goes. +.TP +.B >&2 +Redirect standard output to where standard error goes. +.TP +.BR &> " \fIfile\fP, " &>> " \fIfile\fP" +Write (or append) standard output and standard error to +.IR file . +.PP +Redirections are evaluated from left to right. Only file descriptors 0, 1 +and 2 are supported. +.SS Control structures +.nf +\fBif\fP \fIlist\fP; \fBthen\fP \fIlist\fP; [\fBelif\fP \fIlist\fP; \fBthen\fP \fIlist\fP;] [\fBelse\fP \fIlist\fP;] \fBfi\fP +\fBfor\fP \fIname\fP \fBin\fP \fIwords\fP; \fBdo\fP \fIlist\fP; \fBdone\fP +\fBwhile\fP \fIlist\fP; \fBdo\fP \fIlist\fP; \fBdone\fP +\fBuntil\fP \fIlist\fP; \fBdo\fP \fIlist\fP; \fBdone\fP +\fB!\fP \fIcommand\fP +.fi +.PP +Control structures may span several lines; an incomplete command shows +the continuation prompt +.BR "> " . +.B ! +negates the exit status of a command. Control structures cannot be used +in pipes, with redirections or in the background. +.B case +and functions are not supported. +.SS Aliases +In interactive shells, the first word of a command is replaced by its +alias. If the alias value ends with a blank, the next word is checked for +an alias as well. +.B \e\fIname\fP +or +.B \(aq\fIname\fP\(aq +skips the alias. Aliases are not used in scripts and with +.BR \-c . +.SH BUILTIN COMMANDS +.TP +.BR cd " [\fIdir\fP]" +Change the directory; without argument to the home directory. +.TP +.B pwd +Print the working directory. +.TP +.BR echo " [\fIargs\fP]" +Print the arguments. +.TP +.BR exit " [\fIcode\fP], " quit " [\fIcode\fP]" +Exit the shell with status +.I code +(default: +.BR $? ). +.TP +.BI set " VAR" = value +Set a variable. +.TP +.BI export " VAR" = value +Export a variable. +.TP +.BI unset " VAR" +Remove a variable. +.TP +.BI source " file\fR, \fP" ". " file +Run the commands of +.I file +in the current shell. +.TP +.B jobs +List background and stopped jobs. +.TP +.BR fg " [\fB%\fP\fIn\fP]" +Continue job +.I n +(default: the current job) in the foreground. +.TP +.BR bg " [\fB%\fP\fIn\fP]" +Continue stopped job +.I n +in the background. +.TP +.BR wait " [\fB%\fP\fIn\fP]" +Wait for job +.I n +or for all background jobs. +.TP +.BR alias " [\fIname\fP[\fB=\fP\fIvalue\fP] ...]" +Define or show aliases. +.TP +.BR unalias " \fIname\fP, " "unalias \-a" +Remove an alias, or all aliases. +.TP +.B : +Do nothing, exit status 0. +.TP +.BR break " [\fIn\fP], " continue " [\fIn\fP]" +Leave the innermost (or +.IR n -th) +loop, or continue with its next iteration. +.TP +.B help +Show a summary of builtins and features. +.SH JOB CONTROL +In interactive shells, each command line runs as a job in its own process +group. +.B Ctrl+Z +stops the foreground job, +.B Ctrl+C +interrupts it. +.BR jobs , +.BR fg , +.B bg +and +.B wait +manage the jobs; +.B %n +or +.I n +refers to job +.IR n , +.B %\- +to the previous job. Finished background jobs are reported before the +next prompt. Exiting with stopped jobs warns once. +.SH INTERACTIVE USE +.TP +.B Up/Down +Browse the command history. +.TP +.B Tab +Complete commands, aliases and file names. +.TP +.B Ctrl+C +Cancel the current line or command. +.TP +.B Ctrl+D +Exit the shell. +.PP +Lines starting with a space are not saved in the history. +.PP +The prompt shows +.IR user @ host : dir +followed by +.BR $ . +If the last command failed, its exit status is shown as +.BI [ N ] +before the +.BR $ . +.SH ENVIRONMENT +.TP +.B GOSH_HISTFILE +Path of the history file. If set to an empty value, no history is saved. +.TP +.B XDG_STATE_HOME +Base directory of the default history file +.IR $XDG_STATE_HOME/gosh/history . +.TP +.B GOSH_COLORS +Prompt colors as colon-separated +.IB key = code +entries. Keys are +.B user +(default +.BR 1;32 ), +.B root +.RB ( 1;31 ), +.B path +.RB ( 1;34 ) +and +.B error +.RB ( 1;31 ); +values are ANSI SGR codes, an empty value disables the color. Example: +.B GOSH_COLORS=\(aquser=1;33:path=36\(aq +.TP +.B NO_COLOR +If set, the prompt is not colored. Colors are also disabled with +.B TERM=dumb +or when the output is not a terminal. +.TP +.B HOME +Used for +.B ~ +and +.B cd +without argument. +.TP +.B PATH +Directories searched for commands. +.SH FILES +.TP +.I /etc/gosh/profile +System-wide profile for login shells. +.TP +.I ~/.gosh_profile +Personal profile for login shells. +.TP +.I /etc/gosh/goshrc +System-wide startup file for interactive shells. +.TP +.I ~/.goshrc +Personal startup file for interactive shells. +.TP +.I ~/.local/state/gosh/history +Default history file (mode 0600). +.SH EXIT STATUS +The exit status of the last command, or the argument of +.BR exit . +Typical values: +.TP +.B 1 +General error. +.TP +.B 2 +Syntax error or invalid option. +.TP +.B 126 +Command not executable. +.TP +.B 127 +Command not found. +.TP +.BI 128+ n +Command terminated by signal +.I n +(130 for Ctrl+C). +.SH EXAMPLES +.nf +gosh \-c \(aqcd /srv && git log | head\(aq +for f in *.log; do gzip "$f"; done +cp config.yaml{,.bak} +make > build.log 2>&1 || echo "failed: $?" +LANG=C sort file +alias ll=\(aqls \-l\(aq +chsh \-s /usr/bin/gosh +.fi +.SH SEE ALSO +.BR sh (1), +.BR bash (1), +.BR chsh (1), +.BR shells (5) +.SH AUTHOR +Florian Walther