如何检查 $PATH 中的重复命令?

如何检查 $PATH 中的重复命令?

例如,youtube-dl 有不同的方法将其安装到计算机上。我无意中以不同的方式下载/安装了它多次,因此导致我的 $PATH 目录、 、 和 中有几个 youtube-dl/usr/local/bin/youtube-dl可执行/home/username/.local/bin/youtube-dl文件/usr/local/bin/youtube-dl。因此,即使我安装了升级版本“2021.01.03”,youtube-dl --version也会显示“2020.07.28”,因为 $PATH 中其他目录中的 youtube-dl 以某种方式覆盖了它。

所以在这里,我想检查 $PATH 中所有已安装的同名文件,这样我就可以立即检查它们的版本,并可以看到哪个是最新的、当前的以及哪个应该删除。那么,有没有办法或 CLI 工具可以做到这一点?谢谢。

答案1

交互式地,我会使用:

ls -l $(type -ap youtube-dl)

查找youtube-dl我的 $PATH 中所有程序的位置和时间戳。

当然,这不适用于名称中包含空格的可执行文件,但youtube-dl不是其中之一。

答案2

为了按顺序访问 PATH 目录并列出将执行的文件的位置,您可以使用以下which命令:

$ which -a command_name

答案3

以下脚本将输出在变量列出的目录中找到的重复可执行文件的列表$PATH

它通过创建一个制表符分隔的两列可执行文件列表来实现此目的,其中第一列是目录路径名,第二列是可执行文件的文件名。

该列表由一个简单的程序读取,awk该程序计算每个可执行文件被找到的次数,并在制表符分隔的字符串中收集每个可执行文件的所有目录路径名。

最后,awk代码输出多次找到其确切名称的每个可执行文件,以及找到它的目录路径名的制表符分隔列表。目录将按照搜索顺序列出,即将从以下目录中选择可执行文件第一的如果在命令行上没有显式路径的情况下使用,则会列出目录(给定 的当前值$PATH)。

#!/bin/sh

# turn off filename globbing, and
# split strings in unquoted variables on colons
set -f; IFS=:

# set the positional parameters to the list of
# directories in the PATH variable
set -- $PATH

# turn on filename globbing again
# (leave IFS as is, it won't affect anything else here)
set +f

# loop over those directories and do the things
for dir do
        for pathname in "$dir"/*; do
                [ -x "$pathname" ] && printf '%s\t%s\n' "$dir" "${pathname##*/}"
        done
done |
awk -F '\t' '
{
        dir[$2] = ( dir[$2] == "" ? $1 : dir[$2] "\t" $1 )
        count[$2]++
}
END {
        for (util in dir)
                if (count[util] > 1)
                        printf "%s\t%s\n", util, dir[util]
}'

示例在我的 OpenBSD 系统上运行,传递输出以column -t对其进行良好格式化,然后对其进行排序:

$ sh script | column -t | sort
banner       /usr/bin                /usr/games
bash         /home/myself/local/bin  /usr/local/bin
bashbug      /home/myself/local/bin  /usr/local/bin
chgrp        /bin                    /usr/sbin
chown        /usr/sbin               /sbin
clang        /usr/bin                /usr/local/bin
clang++      /usr/bin                /usr/local/bin
clang-cpp    /usr/bin                /usr/local/bin
ld.lld       /usr/bin                /usr/local/bin
llvm-config  /usr/bin                /usr/local/bin
python3      /home/myself/local/bin  /usr/local/bin
python3.8    /home/myself/local/bin  /usr/local/bin
sysctl       /usr/sbin               /sbin
zsh          /home/myself/local/bin  /usr/local/bin

输出告诉我,如果我bash从命令行启动,它将从 中获取/home/myself/local/bin,并且我bash/usr/local/bin.

答案4

我想检查所有已安装的同名文件$PATH

这是另一个建议,使用findacross 来$PATH获取文件名并awk匹配重复项。如果需要,您可以将其卷成一行,但为了便于阅读,我将其分散在几行中,

( IFS=:; find $PATH -maxdepth 1 -type f ) |    # See below for alternative without maxdepth
    awk -F/ '
        {
            p[$NF] = p[$NF] "\n" $0;           # Save the pathname ($NF is last component)
            h[$NF] ++                          # Count this instance of the filename
        }
        END {
            for (f in h) {
                if (h[f] >1) { print p[f] }    # Print list iff we have multiple hits
            }
        }
    '

如果你find没有-maxdepth操作,您可以将第一行替换为这个扩展

( IFS=:; for dir in $PATH; do find dir ! -path dir -prune -type f; done ) |

无法阅读但卷起的单行版本

(IFS=:; find $PATH -maxdepth 1 -type f)|awk -F/ '{p[$NF]=p[$NF]"\n"$0;h[$NF]++}END{for(f in h){if(h[f]>1){print p[f]}}}'

相关内容