diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 2afa1093691..cda217f1158 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -248,23 +248,27 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let force_flag = matches.get_flag(OPT_FORCE); - if files.is_empty() && !force_flag { - // Still check by hand and not use clap - // Because "rm -f" is a thing - return Err(RmError::MissingOperand.into()); - } - // If -f(--force) is before any -i (or variants) we want prompts else no prompts let force_prompt_never = force_flag && { let force_index = matches.index_of(OPT_FORCE).unwrap_or(0); - ![OPT_PROMPT_ALWAYS, OPT_PROMPT_ONCE, OPT_INTERACTIVE] - .iter() - .any(|flag| { - matches.value_source(flag) == Some(ValueSource::CommandLine) - && matches.index_of(flag).unwrap_or(0) > force_index - }) + let is_later = |flag: &str| { + matches.value_source(flag) == Some(ValueSource::CommandLine) + && matches.index_of(flag).unwrap_or(0) > force_index + }; + // `--interactive=never` does not cancel an earlier `-f`. + let interactive_cancels_force = is_later(OPT_INTERACTIVE) + && matches + .get_one::(OPT_INTERACTIVE) + .is_some_and(|mode| mode != "never"); + !is_later(OPT_PROMPT_ALWAYS) && !is_later(OPT_PROMPT_ONCE) && !interactive_cancels_force }; + if files.is_empty() && !force_prompt_never { + // Still check by hand and not use clap. + // Because "rm -f" is a thing, but a later -i/-I/--interactive cancels it. + return Err(RmError::MissingOperand.into()); + } + let preserve_root = !matches.get_flag(OPT_NO_PRESERVE_ROOT); let preserve_root_all = matches .get_one::(OPT_PRESERVE_ROOT) diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 2a41460b1cb..3acad56c46e 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -495,7 +495,9 @@ fn test_invalid_symlink() { #[test] fn test_force_no_operand() { - new_ucmd!().arg("-f").succeeds().no_stderr(); + for args in [&["-f"][..], &["-f", "--interactive=never"], &["-i", "-f"]] { + new_ucmd!().args(args).succeeds().no_stderr(); + } } #[test] @@ -503,6 +505,23 @@ fn test_no_operand() { new_ucmd!().fails().usage_error("missing operand"); } +#[test] +fn test_force_then_interactive_no_operand() { + // A later -i, -I or --interactive=always/once cancels -f, + // so the missing operand must be reported. + for args in [ + &["-f", "-i"][..], + &["--force", "-I"], + &["-f", "--interactive=always"], + &["-f", "--interactive=once"], + ] { + new_ucmd!() + .args(args) + .fails() + .usage_error("missing operand"); + } +} + #[test] fn test_verbose_slash() { let (at, mut ucmd) = at_and_ucmd!();