冗長コマンドがあるかどうかを$ PATHを確認するには?

冗長コマンドがあるかどうかを$ PATHを確認するには?

たとえば、youtube-dlをコンピュータにインストールする方法はさまざまです。誤って複数の方法で複数回ダウンロード/インストールしたため、$ PATHディレクトリ、、、および/usr/local/bin/youtube-dlに複数のyoutube-dl実行可能ファイルが作成されました。したがって、アップグレードされたバージョン「2021.01.03」をインストールしても、$ PATHの別のディレクトリにあるyoutube-dlが何とか上書きされたため、「2020.07.28」と表示されます。/home/username/.local/bin/youtube-dl/usr/local/bin/youtube-dlyoutube-dl --version

だからここでは、$ PATHで同じ名前のインストール済みファイルをすべて確認して、そのバージョンをすぐに確認し、どれが最新であるか、最新であるか、どのようなものを削除する必要があるかを確認したいと思います。それでは、これを行う方法やCLIツールはありますか?ありがとうございます。

答え1

インタラクティブには、次を使用します。

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

youtube-dl私の$ PATHで、すべてのプログラムの場所とタイムスタンプを見つけます。

もちろん、名前にスペースが含まれる実行可能ファイルでは機能しませんが、youtube-dlそれらの1つではありません。

答え2

PATH ディレクトリに順次アクセスして実行するファイルの場所を一覧表示するには、次のwhichコマンドを使用します。

$ which -a command_name

答え3

次のスクリプトは、変数によってリストされたディレクトリにある重複実行可能ファイルのリストを出力します$PATH

タブ区切りの2列実行可能ファイルのリストを作成してこれを行います。ここで、最初の列はディレクトリパス名、2番目の列は実行可能ファイルのファイル名です。

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/binbash/usr/local/bin

答え4

同じ名前のインストール済みファイルをすべて確認したいと思います。$PATH

ここに別の提案があります。findacrossを使用して$PATHファイル名を取得し、重複awkエントリと一致させることです。必要に応じて1行にまとめることもできますが、読みやすくするために複数行にわたってマークしました。

( 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]}}}'

関連情報