Merge nucleic/keen-glass-marten-ddlf into dev
This commit is contained in:
@@ -363,6 +363,27 @@ impl Execute for ast::AndOrList {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a failing status from this pipeline re-triggers errexit / the ERR trap.
|
||||
/// Mirrors bash: true for multi-command pipelines and for single commands that are
|
||||
/// simple commands (incl. function calls), subshells, extended tests (`[[ ]]`), or
|
||||
/// arithmetic commands (`(( ))`). False for the looping/grouping compounds (brace
|
||||
/// group, if, case, for, while/until, …) whose inner commands have already had their
|
||||
/// own errexit adjudication — their aggregated status must not re-trigger it.
|
||||
fn errexit_applies_to_pipeline(pipeline: &ast::Pipeline) -> bool {
|
||||
if pipeline.seq.len() != 1 {
|
||||
return true;
|
||||
}
|
||||
match &pipeline.seq[0] {
|
||||
ast::Command::Simple(_) | ast::Command::Function(_) | ast::Command::ExtendedTest(..) => {
|
||||
true
|
||||
}
|
||||
ast::Command::Compound(compound, _) => matches!(
|
||||
compound,
|
||||
ast::CompoundCommand::Subshell(_) | ast::CompoundCommand::Arithmetic(_)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Execute for ast::Pipeline {
|
||||
async fn execute(
|
||||
@@ -401,11 +422,21 @@ impl Execute for ast::Pipeline {
|
||||
// Update exit status.
|
||||
shell.set_last_exit_status(result.exit_code.into());
|
||||
|
||||
// bash applies errexit (and the ERR trap) to a failing pipeline's status only
|
||||
// when that status comes from a command whose failure wasn't already
|
||||
// adjudicated inside a compound body: simple commands (incl. function calls),
|
||||
// subshells, extended tests, arithmetic commands, and real multi-command
|
||||
// pipelines. A brace group / if / case / for / while whose body ends with a
|
||||
// short-circuited AND-OR list (or a `!`-negated pipeline) must NOT re-trigger
|
||||
// errexit on its aggregated status (divergence D3, shell/corpus/DIVERGENCES.md;
|
||||
// observed via tzdata's postinst `which restorecon && restorecon` under set -e).
|
||||
let errexit_eligible = errexit_applies_to_pipeline(self);
|
||||
|
||||
// Fire the ERR trap if the pipeline failed in a non-conditional context.
|
||||
// We reuse `suppress_errexit` here because bash suppresses the ERR trap in
|
||||
// exactly the same contexts it suppresses errexit (conditionals, `!`-prefixed
|
||||
// pipelines, etc.).
|
||||
if !result.is_success() && !params.suppress_errexit && !self.bang {
|
||||
if !result.is_success() && !params.suppress_errexit && !self.bang && errexit_eligible {
|
||||
if shell.traps().handles(crate::traps::TrapSignal::Err) {
|
||||
shell
|
||||
.invoke_trap_handler(crate::traps::TrapSignal::Err, ¶ms)
|
||||
@@ -414,7 +445,7 @@ impl Execute for ast::Pipeline {
|
||||
}
|
||||
|
||||
// Apply errexit if not suppressed (and not negated)
|
||||
if !params.suppress_errexit && !self.bang {
|
||||
if !params.suppress_errexit && !self.bang && errexit_eligible {
|
||||
shell.apply_errexit_if_enabled(&mut result);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user