mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
comm: fix comparison when reading from pipes (#9545)
* comm: fix comparison when reading from pipes Use case is that two files are piped into comm, i.e. in bash comm <(cat file1) <(cat file2) Before the fix, comm reads from the pipes twice. Once in "fn comm" and once in "fn are_files_identical". As such, part of the data is skipped in comparison which leads to wrong output. This is fixed by skipping the file comparison in case one of the files is not a regular file. * comm: add test for reading from pipes
This commit is contained in:
@@ -549,6 +549,7 @@ uutests.workspace = true
|
||||
uucore = { workspace = true, features = [
|
||||
"mode",
|
||||
"entries",
|
||||
"pipes",
|
||||
"process",
|
||||
"signals",
|
||||
"utmpx",
|
||||
|
||||
@@ -136,6 +136,11 @@ pub fn are_files_identical(path1: &Path, path2: &Path) -> io::Result<bool> {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// only proceed if both are regular files
|
||||
if !metadata1.is_file() || !metadata2.is_file() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let file1 = File::open(path1)?;
|
||||
let file2 = File::open(path2)?;
|
||||
|
||||
|
||||
@@ -648,3 +648,42 @@ fn test_comm_eintr_handling() {
|
||||
.stdout_contains("line2")
|
||||
.stdout_contains("line3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_comm_anonymous_pipes() {
|
||||
use std::{io::Write, os::fd::AsRawFd, process};
|
||||
use uucore::pipes::pipe;
|
||||
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
// Open two anonymous pipes
|
||||
let (comm1_reader, mut comm1_writer) = pipe().unwrap();
|
||||
let (comm2_reader, mut comm2_writer) = pipe().unwrap();
|
||||
|
||||
// comm reads the data in chunks
|
||||
// make content large enough, so that at least two chunks are read
|
||||
// default buffer size is 8192, so with 6 characters (5 digits + \n) per line we need to write at least 1366 lines
|
||||
|
||||
// write 1500 lines into comm1: 00000\n00001\n...01500\n
|
||||
let mut content = String::new();
|
||||
for i in 0..1500 {
|
||||
content.push_str(&format!("{i:05}\n"));
|
||||
}
|
||||
assert!(comm1_writer.write_all(content.as_bytes()).is_ok());
|
||||
drop(comm1_writer);
|
||||
|
||||
// write into comm2: 00000\n00001\n...01500\n99999\n
|
||||
content.push_str("99999\n");
|
||||
assert!(comm2_writer.write_all(content.as_bytes()).is_ok());
|
||||
drop(comm2_writer);
|
||||
|
||||
// run comm, showing unique lines in second input
|
||||
let comm1_fd = format!("/proc/{}/fd/{}", process::id(), comm1_reader.as_raw_fd());
|
||||
let comm2_fd = format!("/proc/{}/fd/{}", process::id(), comm2_reader.as_raw_fd());
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&["-13", &comm1_fd, &comm2_fd])
|
||||
.succeeds()
|
||||
.stdout_is("99999\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user