From eec73fc0b36c69db53caa877e5b386fc83d75f80 Mon Sep 17 00:00:00 2001 From: fixfon <12512757+fixfon@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:32:06 +0300 Subject: [PATCH] split: print "creating file" with --verbose in the -n modes --verbose announced the output files for -b, -l and -C but not for any --number mode, because the files are opened through OutFiles, which never called print_creating_file. Announce each file once, right before it is first opened, whether OutFiles opens it up front or lazily (-e). Kth-chunk modes write to stdout and still print nothing. Fixes #15583 --- src/uu/split/src/split.rs | 11 ++++++++ tests/by-util/test_split.rs | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index d683bb34328..f48a4f32162 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -810,6 +810,9 @@ struct OutFile { filename: OsString, maybe_writer: Option, is_new: bool, + /// Whether `--verbose` already reported this file, so that retries and + /// re-opens of the writer stay silent. + announced: bool, } /// A set of output files @@ -853,6 +856,9 @@ impl ManageOutFiles for OutFiles { let maybe_writer = if is_writer_optional { None } else { + if settings.verbose { + print_creating_file(&filename)?; + } let instantiated = settings.instantiate_current_writer(&filename, true); // If there was an error instantiating the writer for a file, // it could be due to hitting the system limit of open files, @@ -873,12 +879,17 @@ impl ManageOutFiles for OutFiles { filename, maybe_writer, is_new: true, + announced: !is_writer_optional, }); } Ok(out_files) } fn instantiate_writer(&mut self, idx: usize, settings: &Settings) -> UResult<&mut Writer> { + if settings.verbose && !self[idx].announced { + print_creating_file(&self[idx].filename)?; + self[idx].announced = true; + } let mut count = 0; // Use-case for doing multiple tries of closing fds: // E.g. split running in parallel to other processes (e.g. another split) doing similar stuff, diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index fb39a91b045..9f2d3d3c20e 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -9,6 +9,7 @@ use rand::{RngExt as _, SeedableRng, rng}; use regex::Regex; #[cfg(target_os = "linux")] use rlimit::Resource; +use rstest::rstest; #[cfg(target_os = "linux")] use std::os::unix::ffi::OsStringExt; use std::path::Path; @@ -2353,3 +2354,58 @@ fn test_obsolete_lines_not_read_after_double_dash() { .fails() .stderr_contains("cannot open '-1' for reading"); } + +#[rstest] +#[case::n_2(&["-n", "2"], "a\nb\n", &["xaa", "xab"])] +#[case::n_l_2(&["-n", "l/2"], "a\nb\n", &["xaa", "xab"])] +#[case::n_r_2(&["-n", "r/2"], "a\nb\n", &["xaa", "xab"])] +#[case::n_3(&["-n", "3"], "a\nb\n", &["xaa", "xab", "xac"])] +#[case::n_l_3(&["-n", "l/3"], "a\nb\n", &["xaa", "xab", "xac"])] +#[case::n_r_3(&["-n", "r/3"], "a\nb\n", &["xaa", "xab", "xac"])] +// Only the files that get created are announced with `-e`. +#[case::elide_n_5(&["-e", "-n", "5"], "a\nb\n", &["xaa", "xab", "xac", "xad"])] +#[case::elide_n_l_3(&["-e", "-n", "l/3"], "a\nb\n", &["xaa", "xab"])] +#[case::elide_n_r_3(&["-e", "-n", "r/3"], "a\nb\n", &["xaa", "xab"])] +// A file written to several times is still announced once. +#[case::rewritten_n_2(&["-n", "2"], "1\n2\n3\n4\n5\n", &["xaa", "xab"])] +#[case::rewritten_n_l_2(&["-n", "l/2"], "1\n2\n3\n4\n5\n", &["xaa", "xab"])] +#[case::rewritten_n_r_2(&["-n", "r/2"], "1\n2\n3\n4\n5\n", &["xaa", "xab"])] +#[case::rewritten_elide_n_l_2(&["-e", "-n", "l/2"], "1\n2\n3\n4\n5\n", &["xaa", "xab"])] +#[case::rewritten_elide_n_r_2(&["-e", "-n", "r/2"], "1\n2\n3\n4\n5\n", &["xaa", "xab"])] +fn test_verbose_number_of_chunks( + #[case] args: &[&str], + #[case] input: &str, + #[case] created: &[&str], +) { + use std::fmt::Write as _; + + let (at, mut ucmd) = at_and_ucmd!(); + let expected = created.iter().fold(String::new(), |mut out, name| { + writeln!(out, "creating file '{name}'").unwrap(); + out + }); + + ucmd.args(args) + .arg("--verbose") + .pipe_in(input) + .succeeds() + .stdout_only(expected); + for name in created { + assert!(at.file_exists(name), "{args:?}: {name} is missing"); + } +} + +#[rstest] +#[case::kth_bytes(&["-n", "1/2"], "abc")] +#[case::kth_lines(&["-n", "l/1/2"], "abcd\n")] +#[case::kth_round_robin(&["-n", "r/1/2"], "abcd\n")] +fn test_verbose_kth_chunk_creates_no_file(#[case] args: &[&str], #[case] chunk: &str) { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.args(args) + .arg("--verbose") + .pipe_in("abcd\n") + .succeeds() + .stdout_only(chunk); + assert!(!at.file_exists("xaa"), "{args:?}: unexpected output file"); +}