Skip to content

Commit ebd8377

Browse files
authored
Merge pull request #8655 from naoNao89/fix/csplit-suppress-matched-empty-final-7286
csplit: create final empty file with --suppress-matched to match GNU (fixes #7286)
2 parents aaf742d + 1afeb7a commit ebd8377

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

src/uu/csplit/src/csplit.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,19 +120,28 @@ where
120120
.enumerate();
121121
let mut input_iter = InputSplitter::new(enumerated_input_lines);
122122
let mut split_writer = SplitWriter::new(options);
123-
let patterns: Vec<patterns::Pattern> = patterns::get_patterns(patterns)?;
124-
let ret = do_csplit(&mut split_writer, patterns, &mut input_iter);
123+
let patterns_vec: Vec<patterns::Pattern> = patterns::get_patterns(patterns)?;
124+
let all_up_to_line = patterns_vec
125+
.iter()
126+
.all(|p| matches!(p, patterns::Pattern::UpToLine(_, _)));
127+
let ret = do_csplit(&mut split_writer, patterns_vec, &mut input_iter);
125128

126129
// consume the rest, unless there was an error
127130
if ret.is_ok() {
128131
input_iter.rewind_buffer();
129132
if let Some((_, line)) = input_iter.next() {
133+
// There is remaining input: create a final split and copy remainder
130134
split_writer.new_writer()?;
131135
split_writer.writeln(&line?)?;
132136
for (_, line) in input_iter {
133137
split_writer.writeln(&line?)?;
134138
}
135139
split_writer.finish_split();
140+
} else if all_up_to_line && options.suppress_matched {
141+
// GNU semantics for integer patterns with --suppress-matched:
142+
// even if no remaining input, create a final (possibly empty) split
143+
split_writer.new_writer()?;
144+
split_writer.finish_split();
136145
}
137146
}
138147
// delete files on error by default

tests/by-util/test_csplit.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,44 @@ fn generate(from: u32, to: u32) -> String {
1212
(from..to).fold(String::new(), |acc, v| format!("{acc}{v}\n"))
1313
}
1414

15+
#[test]
16+
fn test_line_numbers_suppress_matched_final_empty() {
17+
// Repro for #7286
18+
let (at, mut ucmd) = at_and_ucmd!();
19+
ucmd.args(&["--suppress-matched", "-", "2", "4", "6"]) // stdin, split at 2/4/6
20+
.pipe_in("1\n2\n3\n4\n5\n6\n")
21+
.succeeds()
22+
.stdout_only("2\n2\n2\n0\n");
23+
24+
// Expect four files: xx00:"1\n", xx01:"3\n", xx02:"5\n", xx03:""
25+
let count = glob(&at.plus_as_string("xx*"))
26+
.expect("there should be splits created")
27+
.count();
28+
assert_eq!(count, 4);
29+
assert_eq!(at.read("xx00"), "1\n");
30+
assert_eq!(at.read("xx01"), "3\n");
31+
assert_eq!(at.read("xx02"), "5\n");
32+
assert_eq!(at.read("xx03"), "");
33+
}
34+
35+
#[test]
36+
fn test_line_numbers_suppress_matched_final_empty_elided_with_z() {
37+
let (at, mut ucmd) = at_and_ucmd!();
38+
ucmd.args(&["--suppress-matched", "-z", "-", "2", "4", "6"]) // elide empty
39+
.pipe_in("1\n2\n3\n4\n5\n6\n")
40+
.succeeds()
41+
.stdout_only("2\n2\n2\n");
42+
43+
// Expect three files: xx00:"1\n", xx01:"3\n", xx02:"5\n"
44+
let count = glob(&at.plus_as_string("xx*"))
45+
.expect("there should be splits created")
46+
.count();
47+
assert_eq!(count, 3);
48+
assert_eq!(at.read("xx00"), "1\n");
49+
assert_eq!(at.read("xx01"), "3\n");
50+
assert_eq!(at.read("xx02"), "5\n");
51+
}
52+
1553
#[test]
1654
fn test_invalid_arg() {
1755
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);

0 commit comments

Comments
 (0)