如何在 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
.
这里发生的一些事情:
IFS=':' DIRS=($PATH)
bash 会在 设置 IFS 和 DIRS 之前扩展变量 ,所以到那时已经太晚了。
你需要一些疯狂的东西,比如
dirs=()
while IFS= read -r -d: dir || [ "$dir" ]; do dirs+=("$dir"); done <<<"$PATH"
——不,那是错误的。首先扩展 $PATH 变量,然后设置 IFS,然后使用 IFS 拆分 DIRS 元素,因为它没有被引用。
for d in $DIRS; do echo $d; done
要遍历数组的所有元素,您需要
for d in "${dirs[@]}" ...
对于数组,$DIRS
等于${DIRS[0]}
,即第一个条目。
不要使用全大写变量名。覆盖关键系统变量太容易了。
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
。少喊对眼睛也有好处。)
实用程序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
.
这里发生的一些事情:
IFS=':' DIRS=($PATH)
bash 会在 设置 IFS 和 DIRS 之前扩展变量 ,所以到那时已经太晚了。 你需要一些疯狂的东西,比如
dirs=() while IFS= read -r -d: dir || [ "$dir" ]; do dirs+=("$dir"); done <<<"$PATH"
——不,那是错误的。首先扩展 $PATH 变量,然后设置 IFS,然后使用 IFS 拆分 DIRS 元素,因为它没有被引用。
for d in $DIRS; do echo $d; done
要遍历数组的所有元素,您需要
for d in "${dirs[@]}" ...
对于数组,
$DIRS
等于${DIRS[0]}
,即第一个条目。不要使用全大写变量名。覆盖关键系统变量太容易了。
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
。少喊对眼睛也有好处。)