diff --git a/interp/builtin.go b/interp/builtin.go index 0135a6126..ba8ea7ab5 100644 --- a/interp/builtin.go +++ b/interp/builtin.go @@ -54,7 +54,7 @@ func (r *Runner) builtinCode(ctx context.Context, pos syntax.Pos, name string, a r.exitShell = true switch len(args) { case 0: - return r.exit + return r.lastExit case 1: n, err := strconv.Atoi(args[0]) if err != nil { diff --git a/interp/interp.go b/interp/interp.go index 5f6001918..e64737a70 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -435,7 +435,8 @@ type Runner struct { noErrExit bool err error // current shell exit code or fatal error - exit int // current (last) exit status code + exit int // current exit status code + lastExit int // last exit status code exitShell bool // whether the shell needs to exit bgShells errgroup.Group @@ -752,6 +753,7 @@ func (r *Runner) stmt(ctx context.Context, st *syntax.Stmt) { if r.stop(ctx) { return } + r.exit = 0 if st.Background { r2 := r.Subshell() st2 := *st @@ -762,6 +764,7 @@ func (r *Runner) stmt(ctx context.Context, st *syntax.Stmt) { } else { r.stmtSync(ctx, st) } + r.lastExit = r.exit } func (r *Runner) stmtSync(ctx context.Context, st *syntax.Stmt) { @@ -777,9 +780,7 @@ func (r *Runner) stmtSync(ctx context.Context, st *syntax.Stmt) { defer cls.Close() } } - if st.Cmd == nil { - r.exit = 0 - } else { + if st.Cmd != nil { r.cmd(ctx, st.Cmd) } if st.Negated { @@ -825,6 +826,7 @@ func (r *Runner) Subshell() *Runner { opts: r.opts, usedNew: r.usedNew, exit: r.exit, + lastExit: r.lastExit, origStdout: r.origStdout, // used for process substitutions } @@ -1023,7 +1025,6 @@ func (r *Runner) cmd(ctx context.Context, cm syntax.Command) { } } case *syntax.TestClause: - r.exit = 0 if r.bashTest(ctx, x.X, false) == "" && r.exit == 0 { // to preserve exit status code 2 for regex errors, etc r.exit = 1 diff --git a/interp/interp_test.go b/interp/interp_test.go index f1f705921..9e3d55b06 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -232,6 +232,8 @@ var runTests = []runTest{ "for i in 1; do break a; done", "usage: break [n]\nexit status 2 #JUSTERR", }, + {"false; a=b", ""}, + {"false; false &", ""}, // we don't need to follow bash error strings {"exit a", "invalid exit status code: \"a\"\nexit status 2 #JUSTERR"}, diff --git a/interp/vars.go b/interp/vars.go index 8fa9b4aa4..8a6fe35f6 100644 --- a/interp/vars.go +++ b/interp/vars.go @@ -60,7 +60,7 @@ func (r *Runner) lookupVar(name string) expand.Variable { case "@", "*": vr.Kind, vr.List = expand.Indexed, r.Params case "?": - vr.Kind, vr.Str = expand.String, strconv.Itoa(r.exit) + vr.Kind, vr.Str = expand.String, strconv.Itoa(r.lastExit) case "$": vr.Kind, vr.Str = expand.String, strconv.Itoa(os.Getpid()) case "PPID":