如何在 Bash 中的 $PATH 中查找文件?

How to find a file on the $PATH in Bash?

实用程序which会在路径上找到一个程序,但是任意数据文件呢?

我一定是在搜索错误的东西,但我找不到使用标准实用程序执行此操作的任何方法,所以我想编写一个小 Bash 脚本来执行此操作。

$PATH 环境变量用冒号分隔,但尝试设置 IFS=':' 然后迭代结果对我不起作用。到目前为止,这是我得到的:

IFS=':' DIRS=($PATH)
for d in $DIRS; do echo $d; done

此时它只输出我路径中的第一个条目,而不是所有条目。

想法?如果已经有执行此操作的标准命令,则没有理由编写脚本...

使用这个,使用bash 参数扩展find:

find ${PATH//:/\/ } -name 'file'

要使用 bash 循环在 PATH 上查找名为 $name 的文件:

oldIFS=$IFS
IFS=:
for d in $PATH
do
    [ -f "$d/$name" ] && echo "Found $d/$name"
done
IFS=$oldIFS

在许多系统上,例如 debian,which 只是一个 shell 脚本,它使用非常相似的循环。看看 less /usr/bin/which.

这里发生的一些事情:

  1. IFS=':' DIRS=($PATH)

    bash 会在 设置 IFS 和 DIRS 之前扩展变量 ,所以到那时已经太晚了。 你需要一些疯狂的东西,比如

    dirs=()
    while IFS= read -r -d: dir || [ "$dir" ]; do dirs+=("$dir"); done <<<"$PATH"
    

——不,那是错误的。首先扩展 $PATH 变量,然后设置 IFS,然后使用 IFS 拆分 DIRS 元素,因为它没有被引用。

  1. for d in $DIRS; do echo $d; done

    要遍历数组的所有元素,您需要

    for d in "${dirs[@]}" ...
    

    对于数组,$DIRS等于${DIRS[0]},即第一个条目。

  2. 不要使用全大写变量名。覆盖关键系统变量太容易了。

  3. Quote your variables 除非你确切地知道否则会发生什么。

错误不在于赋值,而在于没有遍历数组。

IFS=: dirs=($PATH)
for dir in "${dirs[@]}"; do
    echo "$dir"
done

适合我;或更简洁

printf '%s\n' "${dirs[@]}"

就像您发现的那样,$dirs 只是 returns 数组中的第一项。

要实际遍历这些目录以查找特定文件,也许可以尝试

desired_file_name=  # or whatever
for dir in "${dirs[@]}"; do
    test -e "$dir/$desired_file_name" || continue
    echo "[=12=]: found $dir/$desired_file_name" >&2
done

另一种方法是

find "${dirs[@]}" -name "$desired_file_name"

(私有变量最好小写,所以我把DIRS改成了dirs。少喊对眼睛也有好处。)