Repository: sharkdp/fd (language: rust)

Issue:
Regression: `--exec-batch` no longer preserves status code
Suppose you have a `fd` command targeting at least one file, e.g. a directory `/dir` containing a file `foo`. Then running
```bash
fd foo /dir -X bash -c 'exit 1'
```
succeeds (based on exit code) in 8.4.0, but fails in e.g. 8.3.2. I would expect the latter to be the correct behavior (since #477).

Bisecting shows that #960 (specifically 9fb0c5d372062d6193798519526cb7b14ea24fcc) caused this regression; happy to open a PR if desired. 


Discussion / hints:
Thank you for reporting this and tracking this down. Shame on me for not adding a test back then. We should definitely add a regression test when fixing this.

> happy to open a PR if desired

that would be great

Fix patch:
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index da832236c..33ddaae2b 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -16,7 +16,7 @@ use argmax::Command;
 use once_cell::sync::Lazy;
 use regex::Regex;
 
-use crate::exit_codes::ExitCode;
+use crate::exit_codes::{merge_exitcodes, ExitCode};
 
 use self::command::{execute_commands, handle_cmd_error};
 use self::input::{basename, dirname, remove_extension};
@@ -120,7 +120,7 @@ impl CommandSet {
                     }
                 }
 
-                ExitCode::Success
+                merge_exitcodes(builders.iter().map(|b| b.exit_code()))
             }
             Err(e) => handle_cmd_error(None, e),
         }
@@ -136,6 +136,7 @@ struct CommandBuilder {
     cmd: Command,
     count: usize,
     limit: usize,
+    exit_code: ExitCode,
 }
 
 impl CommandBuilder {
@@ -163,6 +164,7 @@ impl CommandBuilder {
             cmd,
             count: 0,
             limit,
+            exit_code: ExitCode::Success,
         })
     }
 
@@ -196,7 +198,9 @@ impl CommandBuilder {
     fn finish(&mut self) -> io::Result<()> {
         if self.count > 0 {
             self.cmd.try_args(&self.post_args)?;
-            self.cmd.status()?;
+            if !self.cmd.status()?.success() {
+                self.exit_code = ExitCode::GeneralError;
+            }
 
             self.cmd = Self::new_command(&self.pre_args)?;
             self.count = 0;
@@ -204,6 +208,10 @@ impl CommandBuilder {
 
         Ok(())
     }
+
+    fn exit_code(&self) -> ExitCode {
+        self.exit_code
+    }
 }
 
 /// Represents a template that is utilized to generate command strings.


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 50157c435..242d4bc80 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1496,6 +1496,8 @@ fn test_exec_batch() {
             &["foo", "--exec-batch", "echo {}"],
             "[fd error]: First argument of exec-batch is expected to be a fixed executable",
         );
+
+        te.assert_failure_with_error(&["a.foo", "--exec-batch", "bash", "-c", "exit 1"], "");
     }
 }
 
@@ -1551,6 +1553,20 @@ fn test_exec_batch_multi() {
             ],
         ]
     );
+
+    te.assert_failure_with_error(
+        &[
+            "a.foo",
+            "--exec-batch",
+            "echo",
+            ";",
+            "--exec-batch",
+            "bash",
+            "-c",
+            "exit 1",
+        ],
+        "",
+    );
 }
 
 #[test]