wc:Ensure the output order of stdout and stderror remains unchanged. (#9905)

---------

Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
mattsu
2026-01-18 18:57:37 +01:00
committed by GitHub
co-authored by Sylvestre Ledru
parent eb482fb2ee
commit 238b27eddf
2 changed files with 30 additions and 7 deletions
+12 -6
View File
@@ -950,12 +950,13 @@ fn wc(inputs: &Inputs, settings: &Settings) -> UResult<()> {
}
};
let word_count = match word_count_from_input(&input, settings) {
CountResult::Success(word_count) => word_count,
CountResult::Interrupted(word_count, err) => {
show!(err.map_err_context(|| input.path_display()));
word_count
}
// Store any I/O error from reading to print AFTER stats (matches GNU wc behavior)
let (word_count, deferred_error) = match word_count_from_input(&input, settings) {
CountResult::Success(word_count) => (word_count, None),
CountResult::Interrupted(word_count, err) => (
word_count,
Some(err.map_err_context(|| input.path_display())),
),
CountResult::Failure(err) => {
show!(err.map_err_context(|| input.path_display()));
continue;
@@ -970,6 +971,11 @@ fn wc(inputs: &Inputs, settings: &Settings) -> UResult<()> {
show!(err.map_err_context(|| translate!("wc-error-failed-to-print-result", "title" => title.to_string_lossy())));
}
}
// Print deferred error after stats to match GNU wc output order
if let Some(err) = deferred_error {
let _ = io::stdout().flush();
show!(err);
}
}
if settings.total_when.is_total_row_visible(num_inputs) {
+18 -1
View File
@@ -8,7 +8,7 @@ use uutests::at_and_ucmd;
use uutests::new_ucmd;
use uutests::util::vec_of_size;
// spell-checker:ignore (flags) lwmcL clmwL ; (path) bogusfile emptyfile manyemptylines moby notrailingnewline onelongemptyline onelongword weirdchars
// spell-checker:ignore (flags) lwmcL clmwL ; (path) bogusfile emptyfile manyemptylines moby notrailingnewline onelongemptyline onelongword weirdchars ioerrdir
#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
@@ -449,6 +449,23 @@ fn test_read_from_directory_error() {
.stdout_is(STDOUT);
}
#[cfg(unix)]
#[test]
fn test_read_error_order_with_stderr_to_stdout() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("ioerrdir");
let expected = format!(
"{:>7} {:>7} {:>7} ioerrdir\nwc: ioerrdir: Is a directory\n",
0, 0, 0
);
ucmd.arg("ioerrdir")
.stderr_to_stdout()
.fails()
.stdout_only(expected);
}
/// Test that getting counts from nonexistent file is an error.
#[test]
fn test_read_from_nonexistent_file() {