add parameter expansion, arithmetic, set -e/-x/pipefail, trap, eval, exec

- ${#v}, ${v#p} ${v##p} ${v%p} ${v%%p}, ${v/p/r} ${v//p/r} ${v/#p/r}
  ${v/%p/r}, ${v:off:len}, ${v-w} ${v:=w} ${v:+w} ${v:?msg}; nested ${}
- arithmetic expansion $((...)) with the C operators, ** and assignments
- set -e, set -x, set -o pipefail (also as gosh options, passed on to
  subshells); set shows variables, set -o the options
- trap for EXIT and signals; scripts with an EXIT trap run it on
  SIGINT/SIGTERM/SIGHUP too
- eval, exec (also exec > file without a command), $!, wait pid

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Florian WaltherandClaude Opus 5.5 committed 2026-10-04 21:15:24 +02:00
1 parent f66cf538c0
commit 1f507f6562
20 files changed
+2408 -93

No files matched your search

+35 -4
View File
@@ -286,7 +286,16 @@ func (j *job) result() error {
for _, p := range j.procs[:len(j.procs)-1] {
printError(p.err)
}
return j.procs[len(j.procs)-1].err
last := j.procs[len(j.procs)-1].err
if pipefail && exitStatus(last) == 0 {
// The status of the last command that failed
for i := len(j.procs) - 2; i >= 0; i-- {
if err := j.procs[i].err; exitStatus(err) != 0 {
return silent(err)
}
}
}
return last
}
// waitForeground waits for a job in the foreground. If it is stopped, it is
@@ -504,9 +513,12 @@ func builtinBg(args []string, stdout io.Writer) error {
func builtinWait(args []string) error {
jobs := append([]*job(nil), jobTable...)
if len(args) > 1 {
j, err := findJob("wait", args)
if err != nil {
return err
j := jobByPid(args[1])
if j == nil {
var err error
if j, err = findJob("wait", args); err != nil {
return err
}
}
jobs = []*job{j}
}
@@ -523,6 +535,23 @@ func builtinWait(args []string) error {
return err
}
// jobByPid returns the job with a process with the ID in spec (wait $!), or
// nil.
func jobByPid(spec string) *job {
pid, err := strconv.Atoi(spec)
if err != nil {
return nil
}
for _, j := range jobTable {
for _, p := range j.procs {
if p.cmd != nil && p.cmd.Process.Pid == pid {
return j
}
}
}
return nil
}
// exitShell exits the shell, unless there are stopped jobs and the user was
// not warned yet (like bash).
func exitShell(code int) error {
@@ -530,6 +559,8 @@ func exitShell(code int) error {
exitWarned = true
return errors.New("there are stopped jobs")
}
lastStatus = code // $? in the EXIT trap
runExitTrap()
releaseTerminal()
os.Exit(code)
return nil