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:
1 parent
f66cf538c0
commit
1f507f6562
20 files changed
+2408
-93
No files matched your search
@@ -11,7 +11,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -191,8 +190,8 @@ func lexWord(src string, i int) (int, error) {
|
||||
}
|
||||
j = end
|
||||
case ch == '$' && j+1 < len(src) && src[j+1] == '{':
|
||||
if k := strings.IndexByte(src[j:], '}'); k >= 0 {
|
||||
j += k
|
||||
if end := paramEnd(src, j); end >= 0 {
|
||||
j = end
|
||||
}
|
||||
case quote == '"':
|
||||
if ch == '"' {
|
||||
@@ -990,6 +989,7 @@ func (l *listNode) exec() error {
|
||||
err = entry.andOr.exec()
|
||||
}
|
||||
lastStatus = exitStatus(err)
|
||||
runPendingTraps()
|
||||
if isAbort(err) {
|
||||
return err
|
||||
}
|
||||
@@ -997,9 +997,18 @@ func (l *listNode) exec() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// exec runs the and-or list. With set -e, only a failure of the last
|
||||
// pipeline ends the shell.
|
||||
func (a *andOrNode) exec() error {
|
||||
last := a.first
|
||||
if len(a.rest) > 0 {
|
||||
errexitOff++
|
||||
}
|
||||
err := a.first.exec()
|
||||
for _, part := range a.rest {
|
||||
if len(a.rest) > 0 {
|
||||
errexitOff--
|
||||
}
|
||||
for i, part := range a.rest {
|
||||
lastStatus = exitStatus(err)
|
||||
if isAbort(err) {
|
||||
return err
|
||||
@@ -1008,11 +1017,29 @@ func (a *andOrNode) exec() error {
|
||||
continue
|
||||
}
|
||||
printError(err)
|
||||
isLast := i == len(a.rest)-1
|
||||
if !isLast {
|
||||
errexitOff++
|
||||
}
|
||||
err = part.pipeline.exec()
|
||||
if !isLast {
|
||||
errexitOff--
|
||||
}
|
||||
last = part.pipeline
|
||||
}
|
||||
if last == a.lastPipeline() {
|
||||
checkErrexit(last, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *andOrNode) lastPipeline() *pipelineNode {
|
||||
if len(a.rest) == 0 {
|
||||
return a.first
|
||||
}
|
||||
return a.rest[len(a.rest)-1].pipeline
|
||||
}
|
||||
|
||||
// runBackground starts the and-or list as a background job. Pipelines run
|
||||
// like in the foreground, everything else in a subshell.
|
||||
func (a *andOrNode) runBackground() error {
|
||||
@@ -1030,6 +1057,10 @@ func (a *andOrNode) runBackground() error {
|
||||
}
|
||||
|
||||
func (pl *pipelineNode) exec() error {
|
||||
if pl.negate {
|
||||
errexitOff++
|
||||
defer func() { errexitOff-- }()
|
||||
}
|
||||
err := pl.cmd.exec()
|
||||
if !pl.negate || isAbort(err) {
|
||||
return err
|
||||
@@ -1073,7 +1104,9 @@ func (s *simpleNode) run(background bool) error {
|
||||
|
||||
func (n *ifNode) exec() error {
|
||||
for i, cond := range n.conds {
|
||||
errexitOff++
|
||||
err := cond.exec()
|
||||
errexitOff--
|
||||
if isAbort(err) {
|
||||
return err
|
||||
}
|
||||
@@ -1116,7 +1149,9 @@ func (n *whileNode) exec() error {
|
||||
|
||||
var result error
|
||||
for {
|
||||
errexitOff++
|
||||
err := n.cond.exec()
|
||||
errexitOff--
|
||||
if isAbort(err) {
|
||||
return err
|
||||
}
|
||||
@@ -1290,14 +1325,7 @@ func caseMatch(pattern, word string) (bool, error) {
|
||||
if !isGlob {
|
||||
return word == strings.Join(literal, " "), nil
|
||||
}
|
||||
// path.Match does not let * match a /; NUL can be in neither string
|
||||
toNUL := strings.NewReplacer("/", "\x00")
|
||||
ok, err := path.Match(toNUL.Replace(strings.Join(glob, " ")), toNUL.Replace(word))
|
||||
if err != nil {
|
||||
// An invalid pattern only matches itself
|
||||
return word == strings.Join(literal, " "), nil
|
||||
}
|
||||
return ok, nil
|
||||
return globMatch(strings.Join(glob, " "), word), nil
|
||||
}
|
||||
|
||||
// expandWordList expands the words of a for loop like the arguments of a
|
||||
|
||||
Reference in new issue
Block a user