6

说我有:

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

我想要类似的东西:

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

今天,这个功能可以为我节省很多时间。我应该早点猜到这会发生,但一开始我并不清楚这个问题,我开始朝完全相反的方向挖掘。

4

2 回答 2

6

好吧,你可以做到,但这并不像你想象的那么容易。

首先,您需要创建一个函数来检查 PATH 中的所有目录,并在那里查找您尝试运行的命令。然后您需要将此函数绑定到当前 shell 的 DEBUG 陷阱。

我写了一个小脚本来做到这一点:

$ 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

使用示例:

$ . /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
$ 
于 2012-07-11T13:08:03.230 回答
2

这是可能的,尽管概括起来有点技巧。请参阅我对https://unix.stackexchange.com/q/42579/20437的回答以了解historyPROMPT_COMMAND魔术。你的 checkSanity 函数看起来像这样:

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
}

但这将在命令完成后打印警告,而不是在开始时。改用 DEBUG 陷阱来获得想要的结果:

trap 'checkSanity "$BASH_COMMAND"' DEBUG
于 2012-07-11T13:05:02.650 回答