diff --git a/src/uu/wc/src/wc.rs b/src/uu/wc/src/wc.rs index 1f4b67c2047..4ae07fe2b8f 100644 --- a/src/uu/wc/src/wc.rs +++ b/src/uu/wc/src/wc.rs @@ -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) { diff --git a/tests/by-util/test_wc.rs b/tests/by-util/test_wc.rs index d1266e09d5c..be23742838f 100644 --- a/tests/by-util/test_wc.rs +++ b/tests/by-util/test_wc.rs @@ -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() {