coreutils icon indicating copy to clipboard operation
coreutils copied to clipboard

rm: shouldn't prompt when stdin is not interactive

Open jfinkels opened this issue 10 months ago • 3 comments

Environment: Ubuntu 24.04, uutils main branch (git commit a2af5d60b5769717a4f40592e10aa6653533328f), GNU coreutils v9.6.19-7386c-modified

Steps to reproduce:

mkdir -m0 dir
rm -d dir < /dev/null

What happens now: uutils rm prints the interactive prompt but immediately terminates, and leaves the directory as-is:

rm: attempt removal of inaccessible directory 'inacc2'?

What I expected to happen: GNU rm prints nothing and removes the directory silently.

Notes: this is causing a failure in GNU test file tests/rm/empty-inacc.sh. I guess the logic here is that if stdin is not an interactive terminal, then the prompting is disabled? I'm not sure.

jfinkels avatar Feb 19 '25 23:02 jfinkels

The current remove_dir function in in src/uu/rm/src/rm.rs calls prompt_dir to decide whether to proceed with removal. Problem is it does not check if stdin is a terminal, so it attempts to prompt even when stdin is /dev/null, leading to immediate termination without removal. To fix this do:

  1. Add a check using atty::is(Stream::Stdin) to detect if stdin is a terminal.
  2. Skip prompting and proceed with removal when stdin is not a terminal, matching GNU rm’s behavior. Here’s the fully rewritten remove_dir function with the fix - (I made the change in here):
/// Remove the given directory, asking the user for permission if necessary.
///
/// Returns true if it has encountered an error.
fn remove_dir(path: &Path, options: &Options) -> bool {
    // Skip prompting if stdin is not a terminal (e.g., redirected from /dev/null)
    if !atty::is(atty::Stream::Stdin) {
        match fs::remove_dir(path) {
            Ok(_) => {
                if options.verbose {
                    println!("removed directory {}", normalize(path).quote());
                }
                return false;
            }
            Err(e) => {
                let e = e.map_err_context(|| format!("cannot remove {}", path.quote()));
                show_error!("{e}");
                return true;
            }
        }
    }
    // Ask the user for permission if stdin is a terminal
    if !prompt_dir(path, options) {
        return false;
    }
    // Called to remove a symlink_dir (windows) without "-r"/"-R" or "-d".
    if !options.dir && !options.recursive {
        show_error!("cannot remove {}: Is a directory", path.quote());
        return true;
    }
    // Try to remove the directory.
    match fs::remove_dir(path) {
        Ok(_) => {
            if options.verbose {
                println!("removed directory {}", normalize(path).quote());
            }
            false
        }
        Err(e) => {
            let e = e.map_err_context(|| format!("cannot remove {}", path.quote()));
            show_error!("{e}");
            true
        }
    }
}

Chrispin-m avatar Feb 20 '25 01:02 Chrispin-m

@Chrispin-m Can you open a PR to contribute your patch ?

RenjiSann avatar Feb 20 '25 12:02 RenjiSann

PR #7492 has been opened to silently proceed through prompts when stdin is not an interactive terminal

bitspill avatar Mar 19 '25 07:03 bitspill