Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 38 additions & 15 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,15 @@ async fn run_capturing(
proc: &mut Process,
command_line: &parser::CommandLine,
) -> String {
run_capturing_status(os, proc, command_line).await.0
}

/// [`run_capturing`], also returning the exit status of the last item it ran.
async fn run_capturing_status(
os: Arc<dyn Kernel>,
proc: &mut Process,
command_line: &parser::CommandLine,
) -> (String, i32) {
let mut output = String::new();
let mut last_exit = 0;
let mut skip = false;
Expand All @@ -1576,8 +1585,8 @@ async fn run_capturing(
(exit, out)
}
Item::Group(cl) => {
let s = Box::pin(run_capturing(os.clone(), proc, cl)).await;
(0, s)
let (s, exit) = Box::pin(run_capturing_status(os.clone(), proc, cl)).await;
(exit, s)
}
Item::Subshell(cl) => {
let mut sub = proc.fork();
Expand All @@ -1586,8 +1595,9 @@ async fn run_capturing(
sub.err_msg(msg);
(1, String::new())
} else {
let s = Box::pin(run_capturing(os.clone(), &mut sub, cl)).await;
(0, s)
let (s, exit) =
Box::pin(run_capturing_status(os.clone(), &mut sub, cl)).await;
(exit, s)
}
}
Item::If {
Expand All @@ -1599,43 +1609,54 @@ async fn run_capturing(
for (cond, body) in branches {
let exit = Box::pin(execute_command_line(os.clone(), proc, cond)).await;
if exit == 0 {
let s = Box::pin(run_capturing(os.clone(), proc, body)).await;
result = (0, s);
let (s, exit) =
Box::pin(run_capturing_status(os.clone(), proc, body)).await;
result = (exit, s);
matched = true;
break;
}
}
if !matched && let Some(body) = else_body {
let s = Box::pin(run_capturing(os.clone(), proc, body)).await;
result = (0, s);
let (s, exit) =
Box::pin(run_capturing_status(os.clone(), proc, body)).await;
result = (exit, s);
}
result
}
Item::While { condition, body } | Item::Until { condition, body } => {
let is_until = matches!(item, Item::Until { .. });
let mut s = String::new();
let mut exit = 0;
loop {
let cond_exit =
Box::pin(execute_command_line(os.clone(), proc, condition)).await;
if (is_until && cond_exit == 0) || (!is_until && cond_exit != 0) {
break;
}
s.push_str(&Box::pin(run_capturing(os.clone(), proc, body)).await);
let (out, body_exit) =
Box::pin(run_capturing_status(os.clone(), proc, body)).await;
s.push_str(&out);
exit = body_exit;
}
(0, s)
(exit, s)
}
Item::For { var, words, body } => {
let expanded = expand_words(os.clone(), proc, words).await;
let mut s = String::new();
let mut exit = 0;
for val in &expanded {
proc.set_env(var, val);
s.push_str(&Box::pin(run_capturing(os.clone(), proc, body)).await);
let (out, body_exit) =
Box::pin(run_capturing_status(os.clone(), proc, body)).await;
s.push_str(&out);
exit = body_exit;
}
(0, s)
(exit, s)
}
Item::Case { word, arms } => {
let value = expand_word(os.clone(), proc, word).await;
let mut s = String::new();
let mut exit = 0;
for arm in arms {
let mut matched = false;
for pat in &arm.patterns {
Expand All @@ -1646,11 +1667,12 @@ async fn run_capturing(
}
}
if matched {
s = Box::pin(run_capturing(os.clone(), proc, &arm.body)).await;
(s, exit) =
Box::pin(run_capturing_status(os.clone(), proc, &arm.body)).await;
break;
}
}
(0, s)
(exit, s)
}
Item::CompoundPipeline {
compound,
Expand Down Expand Up @@ -1736,6 +1758,7 @@ async fn run_capturing(
}
};
last_exit = exit;
proc.last_exit = exit;
output.push_str(&out);
if proc.max_output > 0 && output.len() > proc.max_output {
proc.err_msg("strands-shell: output size limit exceeded");
Expand All @@ -1745,7 +1768,7 @@ async fn run_capturing(
}
}
}
output
(output, last_exit)
}

/// Public-facing execute_command_line that swallows break/continue.
Expand Down
202 changes: 202 additions & 0 deletions tests/shell_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3960,6 +3960,208 @@ expect!(
"x y z"
);

// ── exec: compound status in command substitution ──────────────────
expect!(
cmd_subst_group_false_and,
"X=$({ false; } && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_group_false_or,
"X=$({ false; } || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_group_false_status,
"X=$({ false; }; echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_group_true_and,
"X=$({ true; } && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_group_true_or,
"X=$({ true; } || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_subshell_false_and,
"X=$( (false) && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_subshell_false_or,
"X=$( (false) || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_subshell_false_status,
"X=$( (false); echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_subshell_true_and,
"X=$( (true) && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_subshell_true_or,
"X=$( (true) || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_if_false_and,
"X=$(if true; then false; fi && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_if_false_or,
"X=$(if true; then false; fi || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_if_false_status,
"X=$(if true; then false; fi; echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_if_true_and,
"X=$(if true; then true; fi && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_if_true_or,
"X=$(if true; then true; fi || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_for_false_and,
"X=$(for i in 1; do false; done && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_for_false_or,
"X=$(for i in 1; do false; done || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_for_false_status,
"X=$(for i in 1; do false; done; echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_for_true_and,
"X=$(for i in 1; do true; done && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_for_true_or,
"X=$(for i in 1; do true; done || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_while_false_and,
"X=$(n=0; while [ $n -lt 1 ]; do n=1; false; done && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_while_false_or,
"X=$(n=0; while [ $n -lt 1 ]; do n=1; false; done || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_while_false_status,
"X=$(n=0; while [ $n -lt 1 ]; do n=1; false; done; echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_while_true_and,
"X=$(n=0; while [ $n -lt 1 ]; do n=1; true; done && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_while_true_or,
"X=$(n=0; while [ $n -lt 1 ]; do n=1; true; done || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_until_false_and,
"X=$(n=0; until [ $n -ge 1 ]; do n=1; false; done && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_until_false_or,
"X=$(n=0; until [ $n -ge 1 ]; do n=1; false; done || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_until_false_status,
"X=$(n=0; until [ $n -ge 1 ]; do n=1; false; done; echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_until_true_and,
"X=$(n=0; until [ $n -ge 1 ]; do n=1; true; done && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_until_true_or,
"X=$(n=0; until [ $n -ge 1 ]; do n=1; true; done || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_case_false_and,
"X=$(case a in (a) false;; esac && echo yes); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_case_false_or,
"X=$(case a in (a) false;; esac || echo no); echo \"[$X]\"",
"[no]"
);
expect!(
cmd_subst_case_false_status,
"X=$(case a in (a) false;; esac; echo $?); echo \"[$X]\"",
"[1]"
);
expect!(
cmd_subst_case_true_and,
"X=$(case a in (a) true;; esac && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_case_true_or,
"X=$(case a in (a) true;; esac || echo no); echo \"[$X]\"",
"[]"
);
expect!(
cmd_subst_group_last_status_wins,
"X=$({ false; true; } && echo yes; { true; false; } || echo no); echo \"[$X]\"",
"[yes\nno]"
);
expect!(
cmd_subst_if_unmatched_is_success,
"X=$(if false; then false; fi && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_empty_loop_is_success,
"X=$(false; for i in; do false; done && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_case_unmatched_is_success,
"X=$(false; case a in (b) false;; esac && echo yes); echo \"[$X]\"",
"[yes]"
);
expect!(
cmd_subst_pipeline_status,
"X=$(false; echo $?); echo \"[$X]\"",
"[1]"
);

// ── exec: shebang / script execution ──────────────────────────────
expect!(
script_shebang_source,
Expand Down
Loading