From 85e5fc28754849ce690ff583249381f9c7a91fb9 Mon Sep 17 00:00:00 2001 From: pagedmov Date: Mon, 9 Mar 2026 21:55:03 -0400 Subject: [PATCH] Fork non-command nodes for background jobs, fix interactive flag in child processes, and add empty variable test for `[` builtin --- src/builtin/autocmd.rs | 2 +- src/main.rs | 2 +- src/parse/execute.rs | 24 ++++++++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/builtin/autocmd.rs b/src/builtin/autocmd.rs index 7f6fc14..2b2ee55 100644 --- a/src/builtin/autocmd.rs +++ b/src/builtin/autocmd.rs @@ -250,7 +250,7 @@ mod tests { "pre-mode-change", "post-mode-change", "on-history-open", "on-history-close", "on-history-select", "on-completion-start", "on-completion-cancel", "on-completion-select", - "on-exit", + "on-exit" ]; for kind in kinds { test_input(format!("autocmd {kind} 'true'")).unwrap(); diff --git a/src/main.rs b/src/main.rs index fc0a8e5..79a9984 100644 --- a/src/main.rs +++ b/src/main.rs @@ -249,7 +249,7 @@ fn shed_interactive(args: ShedArgs) -> ShResult<()> { // may have moved it during resize/rewrap readline.writer.update_t_cols(); readline.mark_dirty(); - } + } if JOB_DONE.swap(false, Ordering::SeqCst) { // update the prompt so any job count escape sequences update dynamically diff --git a/src/parse/execute.rs b/src/parse/execute.rs index 31c0d77..6904d36 100644 --- a/src/parse/execute.rs +++ b/src/parse/execute.rs @@ -767,8 +767,16 @@ impl Dispatcher { self.job_stack.new_job(); if cmds.len() == 1 { self.fg_job = !is_bg && self.interactive; - let cmd = cmds.into_iter().next().unwrap(); - self.dispatch_node(cmd)?; + let mut cmd = cmds.into_iter().next().unwrap(); + if is_bg && !matches!(cmd.class, NdRule::Command { .. }) { + self.run_fork(&cmd.get_command().map(|t| t.to_string()).unwrap_or_default(), |s| { + if let Err(e) = s.dispatch_node(cmd) { + e.print_error(); + } + })?; + } else { + self.dispatch_node(cmd)?; + } // Give the pipeline terminal control as soon as the first child // establishes the PGID, so later children (e.g. nvim) don't get @@ -1103,6 +1111,7 @@ impl Dispatcher { match unsafe { fork()? } { ForkResult::Child => { let _ = setpgid(Pid::from_raw(0), existing_pgid.unwrap_or(Pid::from_raw(0))); + self.interactive = false; f(self); exit(state::get_status()) } @@ -1401,4 +1410,15 @@ mod tests { assert_eq!(state::get_status(), 0); assert_eq!(g.read_output(), "yes\n"); } + + #[test] + fn empty_var_in_test() { + let _g = TestGuard::new(); + // POSIX specifies that a quoted unset variable expands to an empty string, so the shell actually sees `[ -n "" ]`, which returns false + test_input("[ -n \"$EMPTYVAR_PROBABLY_NOT_SET_TO_ANYTHING\" ]").unwrap(); + assert_eq!(state::get_status(), 1); + // Without quotes, word splitting causes an empty var to be removed entirely, so the shell actually sees `[ -n ]`, testing the value of ']', which returns true + test_input("[ -n $EMPTYVAR_PROBABLY_NOT_SET_TO_ANYTHING ]").unwrap(); + assert_eq!(state::get_status(), 0); + } }