简体   繁体   中英

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.

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.

Since you can't know if there is a wildcard, why not check the number of arguments?

For example:

#!/bin/bash
rm () {
    if [ "$#" -gt 1 ]; then
       echo command rm -i "$@"
    else
       echo command rm "$@"
    fi
}
rm a b c
rm a

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM