Search code examples
regexbashwildcardrm

How to capture "*" as argument to bash function and use in a comparison


I want to have bash run rm -i * if I type rm * but otherwise run regular rm. Importantly, I don't want to run rm -i every time I use a wildcard such as rm part*. Here is as far as I could come up with:

rm ()
{
    if [ "$1" == "*" ]; then
        rm -i *
    else
        rm $1
    fi
}

But I know this will fail. I know that the comparison I want is to ^*$, but I don't know how to implement it.


Solution

  • It's literally impossible to know if your command was called with a wildcard without the cooperation of your shell.

    When you invoke rm * (like any other command), the * is replaced with a list of filenames before invocation. Thus, when inside the command, the information that it was given a wildcard no longer exists: $1, $2, etc. have been replaced with a list of names that the wildcard expanded to.


    That said, since we're a shell function, the cooperation of our shell is actually available:

    rm() {
      local cmd
      read -r _ cmd < <(HISTTIMEFORMAT=''; history 1)
      if [[ $cmd = "rm *" ]]; then
        command rm -i "$@"
      else
        command rm "$@"
      fi
    }
    

    How does this work?

    • history 1 returns the most recent command in the shell's history (preceded by a number).
    • read -r _ cmd reads that number into the variable _, and the rest of the command line into the variable cmd
    • [[ $cmd = "rm *" ]] compares the command against that precise string
    • command rm ... runs the external rm command, avoiding recursion back to our function again.