Skip to content

Commit 238b27e

Browse files
wc:Ensure the output order of stdout and stderror remains unchanged. (#9905)
--------- Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
1 parent eb482fb commit 238b27e

2 files changed

Lines changed: 30 additions & 7 deletions

File tree

src/uu/wc/src/wc.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -950,12 +950,13 @@ fn wc(inputs: &Inputs, settings: &Settings) -> UResult<()> {
950950
}
951951
};
952952

953-
let word_count = match word_count_from_input(&input, settings) {
954-
CountResult::Success(word_count) => word_count,
955-
CountResult::Interrupted(word_count, err) => {
956-
show!(err.map_err_context(|| input.path_display()));
957-
word_count
958-
}
953+
// Store any I/O error from reading to print AFTER stats (matches GNU wc behavior)
954+
let (word_count, deferred_error) = match word_count_from_input(&input, settings) {
955+
CountResult::Success(word_count) => (word_count, None),
956+
CountResult::Interrupted(word_count, err) => (
957+
word_count,
958+
Some(err.map_err_context(|| input.path_display())),
959+
),
959960
CountResult::Failure(err) => {
960961
show!(err.map_err_context(|| input.path_display()));
961962
continue;
@@ -970,6 +971,11 @@ fn wc(inputs: &Inputs, settings: &Settings) -> UResult<()> {
970971
show!(err.map_err_context(|| translate!("wc-error-failed-to-print-result", "title" => title.to_string_lossy())));
971972
}
972973
}
974+
// Print deferred error after stats to match GNU wc output order
975+
if let Some(err) = deferred_error {
976+
let _ = io::stdout().flush();
977+
show!(err);
978+
}
973979
}
974980

975981
if settings.total_when.is_total_row_visible(num_inputs) {

tests/by-util/test_wc.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use uutests::at_and_ucmd;
88
use uutests::new_ucmd;
99
use uutests::util::vec_of_size;
1010

11-
// spell-checker:ignore (flags) lwmcL clmwL ; (path) bogusfile emptyfile manyemptylines moby notrailingnewline onelongemptyline onelongword weirdchars
11+
// spell-checker:ignore (flags) lwmcL clmwL ; (path) bogusfile emptyfile manyemptylines moby notrailingnewline onelongemptyline onelongword weirdchars ioerrdir
1212
#[test]
1313
fn test_invalid_arg() {
1414
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
@@ -449,6 +449,23 @@ fn test_read_from_directory_error() {
449449
.stdout_is(STDOUT);
450450
}
451451

452+
#[cfg(unix)]
453+
#[test]
454+
fn test_read_error_order_with_stderr_to_stdout() {
455+
let (at, mut ucmd) = at_and_ucmd!();
456+
at.mkdir("ioerrdir");
457+
458+
let expected = format!(
459+
"{:>7} {:>7} {:>7} ioerrdir\nwc: ioerrdir: Is a directory\n",
460+
0, 0, 0
461+
);
462+
463+
ucmd.arg("ioerrdir")
464+
.stderr_to_stdout()
465+
.fails()
466+
.stdout_only(expected);
467+
}
468+
452469
/// Test that getting counts from nonexistent file is an error.
453470
#[test]
454471
fn test_read_from_nonexistent_file() {

0 commit comments

Comments
 (0)