6

Say I have:

>which -a foo
/bin/foo
/usr/bin/foo

I want something like:

>foo
Warning: multiple foo in PATH
... foo gets executed ...

This functionality would have saved me really really lots of time today. I should have guessed this is happening earlier but the problem was unclear to me at the beggining and I started to dig in quite opposite direction.

simont
  • 68,704
  • 18
  • 117
  • 136
nshy
  • 1,074
  • 8
  • 13

2 Answers2

6

Well, you can do it, but it is not so easy as you may think.

First, you need to create a function that will check all directories in the PATH, and look there for the command you try to run. Then you need to bind this function to the DEBUG trap of your current shell.

I wrote a small script that does that:

$ cat /tmp/1.sh

check_command()
{
    n=0
    DIRS=""
    for i in $(echo $PATH| tr : " ")
    do 
        if [ -x "$i/$1" ]
        then 
            n=$[n+1]
            DIRS="$DIRS $i"
        fi
    done
    if [ "$n" -gt 1 ]
    then
      echo "Warning: there are multiple commands in different PATH directories: "$DIRS
    fi
}

preexec () {
    check_command $1
}
preexec_invoke_exec () {
    [ -n "$COMP_LINE" ] && return  # do nothing if completing
    local this_command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
    preexec "$this_command"
}
trap 'preexec_invoke_exec' DEBUG

Example of usage:

$ . /tmp/1.sh
$ sudo cp /bin/date /usr/bin/test_it
$ sudo cp /bin/date /bin/test_it
$ test_it
Warning: there are multiple commands in different PATH directories:  /usr/bin /bin
Wed Jul 11 15:14:43 CEST 2012
$ 
Igor Chubin
  • 61,765
  • 13
  • 122
  • 144
  • 2
    Rather than iterating over `$PATH`, you can use `type -pa "$1"`. Also you might be able to use `$BASH_COMMAND` instead of parsing `history`. The `preexec` function must serve a purpose in another context, but it's extraneous here. – Dennis Williamson Jul 11 '12 at 14:29
  • Ohh, I thought I would be notified by email on answers to the question. **Thank you.** I guess the short answer would be "use DEBUG trap" ) as other stuff is more obvious bash mashinery. In this regard your answer and @lynxlynxlynx one are both good one. Nethertheless I vote yours as answer just it on top, no other reasons. – nshy Jul 17 '12 at 09:20
2

It is possible, though a bit trick to generalise. See my answer to https://unix.stackexchange.com/q/42579/20437 for the history and PROMPT_COMMAND magic. Your checkSanity function would look something like this:

checkSanity() {
  cmd="${1%% *}" # strip everything but the first word
  candidates=$(which -a $cmd | wc -l)
  if (( candidates > 1 )); then
    echo "Warning: multiple $cmd in PATH"
  fi
}

But that will print the warning after the command finishes, not at the start. Use the DEBUG trap instead to get the wanted result:

trap 'checkSanity "$BASH_COMMAND"' DEBUG
Community
  • 1
  • 1
lynxlynxlynx
  • 1,371
  • 17
  • 26