Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/uu/csplit/src/csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,28 @@ where
.enumerate();
let mut input_iter = InputSplitter::new(enumerated_input_lines);
let mut split_writer = SplitWriter::new(options);
let patterns: Vec<patterns::Pattern> = patterns::get_patterns(patterns)?;
let ret = do_csplit(&mut split_writer, patterns, &mut input_iter);
let patterns_vec: Vec<patterns::Pattern> = patterns::get_patterns(patterns)?;
let all_up_to_line = patterns_vec
.iter()
.all(|p| matches!(p, patterns::Pattern::UpToLine(_, _)));
let ret = do_csplit(&mut split_writer, patterns_vec, &mut input_iter);

// consume the rest, unless there was an error
if ret.is_ok() {
input_iter.rewind_buffer();
if let Some((_, line)) = input_iter.next() {
// There is remaining input: create a final split and copy remainder
split_writer.new_writer()?;
split_writer.writeln(&line?)?;
for (_, line) in input_iter {
split_writer.writeln(&line?)?;
}
split_writer.finish_split();
} else if all_up_to_line && options.suppress_matched {
// GNU semantics for integer patterns with --suppress-matched:
// even if no remaining input, create a final (possibly empty) split
split_writer.new_writer()?;
split_writer.finish_split();
}
}
// delete files on error by default
Expand Down
38 changes: 38 additions & 0 deletions tests/by-util/test_csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,44 @@ fn generate(from: u32, to: u32) -> String {
(from..to).fold(String::new(), |acc, v| format!("{acc}{v}\n"))
}

#[test]
fn test_line_numbers_suppress_matched_final_empty() {
// Repro for #7286
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["--suppress-matched", "-", "2", "4", "6"]) // stdin, split at 2/4/6
.pipe_in("1\n2\n3\n4\n5\n6\n")
.succeeds()
.stdout_only("2\n2\n2\n0\n");

// Expect four files: xx00:"1\n", xx01:"3\n", xx02:"5\n", xx03:""
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
.count();
assert_eq!(count, 4);
assert_eq!(at.read("xx00"), "1\n");
assert_eq!(at.read("xx01"), "3\n");
assert_eq!(at.read("xx02"), "5\n");
assert_eq!(at.read("xx03"), "");
}

#[test]
fn test_line_numbers_suppress_matched_final_empty_elided_with_z() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["--suppress-matched", "-z", "-", "2", "4", "6"]) // elide empty
.pipe_in("1\n2\n3\n4\n5\n6\n")
.succeeds()
.stdout_only("2\n2\n2\n");

// Expect three files: xx00:"1\n", xx01:"3\n", xx02:"5\n"
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
.count();
assert_eq!(count, 3);
assert_eq!(at.read("xx00"), "1\n");
assert_eq!(at.read("xx01"), "3\n");
assert_eq!(at.read("xx02"), "5\n");
}

#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
Expand Down
Loading