diff --git a/src/uu/sort/src/ext_sort/threaded.rs b/src/uu/sort/src/ext_sort/threaded.rs index 7dd089d0fe8..3cb45198cff 100644 --- a/src/uu/sort/src/ext_sort/threaded.rs +++ b/src/uu/sort/src/ext_sort/threaded.rs @@ -8,13 +8,15 @@ use std::cmp::Ordering; use std::fs::File; -use std::io::{Read, Write, stderr}; +use std::io::{Read, Write}; use std::path::PathBuf; use std::sync::mpsc::{Receiver, SyncSender}; use std::thread; use itertools::Itertools; use uucore::error::{UResult, strip_errno}; +use uucore::show_error; +use uucore::translate; use crate::Output; use crate::chunks::RecycledChunk; @@ -52,6 +54,8 @@ pub fn ext_sort( // Test if compression program exists and works, disable if not let mut effective_settings = settings.clone(); + // Keep the error until we know compression is actually needed. + let mut compress_prog_error = None; if let Some(ref prog) = settings.compress_prog { // Test the compression program by trying to spawn it match std::process::Command::new(prog) @@ -65,12 +69,7 @@ pub fn ext_sort( let _ = child.kill(); } Err(err) => { - // Print the error and disable compression - let _ = writeln!( - stderr(), - "sort: could not run compress program '{prog}': {}", - strip_errno(&err) - ); + compress_prog_error = Some((prog.clone(), err)); effective_settings.compress_prog = None; } } @@ -80,6 +79,7 @@ pub fn ext_sort( reader_writer::<_, WriteableCompressedTmpFile>( files, &effective_settings, + None, &sorted_receiver, recycled_sender, output, @@ -89,6 +89,7 @@ pub fn ext_sort( reader_writer::<_, WriteablePlainTmpFile>( files, &effective_settings, + compress_prog_error, &sorted_receiver, recycled_sender, output, @@ -103,6 +104,7 @@ fn reader_writer< >( files: F, settings: &GlobalSettings, + compress_prog_error: Option<(String, std::io::Error)>, receiver: &Receiver, sender: SyncSender, output: Output, @@ -125,6 +127,7 @@ fn reader_writer< separator, buffer_size, settings, + compress_prog_error, receiver, sender, )?; @@ -207,12 +210,14 @@ enum ReadResult { WroteChunksToFile { tmp_files: Vec }, } /// The function that is executed on the reader/writer thread. +#[allow(clippy::too_many_arguments)] fn read_write_loop( mut files: impl Iterator>>, tmp_dir: &mut TmpDirWrapper, separator: u8, buffer_size: usize, settings: &GlobalSettings, + compress_prog_error: Option<(String, std::io::Error)>, receiver: &Receiver, sender: SyncSender, ) -> UResult> { @@ -249,6 +254,18 @@ fn read_write_loop( } } + // The input did not fit into the first two in-memory chunks. + if let Some((prog, err)) = compress_prog_error { + show_error!( + "{}", + translate!( + "sort-compress-prog-execution-failed", + "prog" => prog, + "error" => strip_errno(&err) + ) + ); + } + let mut sender_option = Some(sender); let mut tmp_files = vec![]; loop { diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index c5e463b7654..f5815935691 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -1495,8 +1495,9 @@ fn test_compress_fail() { "-n", "--compress-program", "nonexistent-program", + // Force external sorting by using 1K buffer size so ext_sort.txt spills to disk "-S", - "10", + "1K", ]) .succeeds(); @@ -1516,6 +1517,20 @@ fn test_compress_fail() { assert_eq!(result.stdout_str(), expected); } +#[test] +#[cfg(unix)] +fn test_input_error_before_compression_is_needed() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("input", "b\na\n"); + at.mkdir("directory"); + + ucmd.args(&["--compress-program=nonexistent", "input", "directory"]) + .fails_with_code(2) + .no_stdout() + .stderr_contains("Is a directory") + .stderr_does_not_contain("compress program"); +} + #[test] fn test_merge_batches() { new_ucmd!()