diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index e7b00838e..2d36679f0 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -649,6 +649,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let summarize = matches.get_flag(options::SUMMARIZE); + let count_links = matches.get_flag(options::COUNT_LINKS); + let max_depth = parse_depth( matches .get_one::(options::MAX_DEPTH) @@ -669,15 +671,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } read_files_from(file_from)? - } else { - match matches.get_one::(options::FILE) { - Some(_) => matches - .get_many::(options::FILE) - .unwrap() - .map(PathBuf::from) - .collect(), - None => vec![PathBuf::from(".")], + } else if let Some(files) = matches.get_many::(options::FILE) { + let files = files.map(PathBuf::from); + if count_links { + files.collect() + } else { + // Deduplicate while preserving order + let mut seen = std::collections::HashSet::new(); + files + .filter(|path| seen.insert(path.clone())) + .collect::>() } + } else { + vec![PathBuf::from(".")] }; let time = matches.contains_id(options::TIME).then(|| { @@ -719,7 +725,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else { Deref::None }, - count_links: matches.get_flag(options::COUNT_LINKS), + count_links, verbose: matches.get_flag(options::VERBOSE), excludes: build_exclude_patterns(&matches)?, }; diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 84e3b5050..ecbf58b11 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -1194,3 +1194,61 @@ fn test_human_size() { .succeeds() .stdout_contains(format!("1.0K {dir}")); } + +#[cfg(not(target_os = "android"))] +#[test] +fn test_du_deduplicated_input_args() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.mkdir("d"); + at.mkdir("d/d"); + at.touch("d/f"); + at.hard_link("d/f", "d/h"); + + let result = ts + .ucmd() + .arg("--inodes") + .arg("d") + .arg("d") + .arg("d") + .succeeds(); + result.no_stderr(); + + let result_seq: Vec = result + .stdout_str() + .lines() + .map(|x| x.parse().unwrap()) + .collect(); + #[cfg(windows)] + assert_eq!(result_seq, ["1\td\\d", "3\td"]); + #[cfg(not(windows))] + assert_eq!(result_seq, ["1\td/d", "3\td"]); +} + +#[cfg(not(target_os = "android"))] +#[test] +fn test_du_no_deduplicated_input_args() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.mkdir("d"); + at.touch("d/d"); + + let result = ts + .ucmd() + .arg("--inodes") + .arg("-l") + .arg("d") + .arg("d") + .arg("d") + .succeeds(); + result.no_stderr(); + + let result_seq: Vec = result + .stdout_str() + .lines() + .map(|x| x.parse().unwrap()) + .collect(); + assert_eq!(result_seq, ["2\td", "2\td", "2\td"]); +}