abalter abalter - 4 months ago 6
Bash Question

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.

Answer

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.
Comments