パス型ファイルの検索

パス型ファイルの検索

デフォルトではsearchコマンドに似たものを取得しようとしていますが、ディレクトリのリストが$PATH異なり、検索中のファイルが実行可能ではありません。

ディレクトリリストは固定されており(PATHと同じ形式の変数に圧縮できます)、サブディレクトリにドロップダウンしたくありません。 bashにcompleteオプションがあることを覚えていますが、そのセクションを最後まで読んでいましたが、見つかりませんでした。私は近いですか?

たぶん、次のようなものがあります(ファイルがあるとします~/lib)。

> filepath -p ".:/etc:$HOME/lib:$HOME/bin" foo
/home/alexis/lib/foo

whichつまり、orのようなものを探していますが、which -a実行可能ファイルに限定されてはいけません。実行可能ファイルだけを検索してはいけませんが、$PATH問題を解決できます;-)。

答え1

findすでに複数の検索パスをサポートしています。

find . /etc ~/lib ~/bin -maxdepth 1 -name '*foo*'

または使用fdfindよりも速く、機能が豊富で、より美しいです。

fd --max-depth=1 foo . /etc ~/lib ~/bin

別の簡単な方法ですが、空白や改行のあるファイルでは機能しない可能性があります。

SearchPaths=(/etc ~/lib ~/bin)
shopt -s nullglob
eval printf "%s " "${SearchPaths[@]/%/*foo*}"

Bash配列を使って直接ファイル拡張を実行するより簡単な方法を見たようですが、今は覚えていません。

答え2

bash関数はGNUのラッパーとして使用できますfind

filepath () {
    local fp_path=${FP_PATH:-".:/etc:$HOME/lib:$HOME/bin"} fpath fquit=-quit
    local usage="usage: filepath [-a] [-p path[:path...]] expr"

    OPTIND=1
    while getopts :ap: opt; do
        case $opt in
            a)  fquit=
                ;;
            p)  fp_path=$OPTARG
                ;;
            *)  echo "$usage" >&2
                return 1
        esac
    done
    shift $((OPTIND-1))

    if [ -z "$1" ]; then
        echo "$usage" >&2
        return 1
    fi

    IFS=: read -r -a fpath <<<"$fp_path"
    find "${fpath[@]}" -maxdepth 1 \( -type f -o -type l \) -name "$1" -print $fquit 2>/dev/null
}

デフォルトの検索パスは関数の最初の行で定義され、:区切り文字として使用されます。環境変数FP_PATHまたはオプションでオーバーライドできます-p。オプションを使用しない限り、
find最初の結果を印刷して終了します。-a検索は通常のファイルとシンボリックリンクに制限され、必要に応じてオプションを調整します。

例:

$ mkdir -p ~/bin /tmp/etc
$ touch ~/bin/foo /tmp/foo /tmp/etc/foo
$ filepath
usage: filepath [-a] [-p path[:path...]] expr

$ filepath -a -p /tmp:/tmp/etc 'fo*'
/tmp/foo
/tmp/etc/foo

$ filepath -p /tmp:/tmp/etc 'fo*'
/tmp/foo

$ FP_PATH=/tmp filepath foo
/tmp/foo

$ filepath foo
/home/freddy/bin/foo

答え3

ディレクトリリストを配列(bash)に保存する場合は、次のコードを使用できます。

# Maybe in ~/.bashrc
filefindlist=(. /etc "$HOME/lib" "$HOME/bin" src/*)

filefind() {
    local ff
    for ff in "${filefindlist[@]}"
    do
        if [ -d "$ff" ] && [ -f "$ff/$1" ]
        then
            printf '%s\n' "$ff/$1"
            return 0
        fi
    done
    return 1
}

使用法

filefind program.c

filefindlist配列を定義すると、すべての変数とシェルグローバル変数(ワイルドカード)が評価されます。したがって、src/one後で配列を作成するだけで拡張されますsrc/two。ただし、次に配列を定義するときは、 と が含まれます。src/*src/onesrc/onesrc/two

質問で提案された内容を正確に実装するには、次のアプローチを使用できます。

filefind() {
    local fl="$FILEFINDLIST" fp ff

    [ "$1" = '-p' ] && fl="$2" && shift 2    # Override default search list
    readarray -d: -t fp <<<"$fl"             # Convert to array

    for ff in "${fp[@]}"
    do
        if [ -d "$ff" ] && [ -f "$ff/$1" ]
        then
            printf '%s\n' "$ff/$1"
            return 0                         # Only print first match
        fi
    done
    return 1
}

使用法

FILEFINDLIST='.:$HOME/lib:$HOME/bin'

filefind -p ".:/etc:$HOME/lib:$HOME/bin" program.c
filefind program.c

答え4

そしてzsh

filepath() print -rC1 -- ${(s[:])^1}/$^@[2,-1](N)

それから:

$ filepath .:/etc:$HOME/lib:$HOME/bin ls issue python
/etc/issue
/home/chazelas/lib/python
/home/chazelas/bin/ls

関連情報