说我有PATH="home/bob/bin:/usr/bin"
。我正在编写一个 bash 脚本/home/bob/bin/foo
,它将进行一些修改,然后调用/usr/bin/foo
.当然,我希望能够在具有不同路径结构的不同系统上使用此脚本。实际上,真正的 foo 可能位于许多不同的地方,所以我想从 PATH 中找到它。我的新 foo 脚本也在我的路径上,所以我不能只调用 foo,这将导致递归调用。
有没有一种简单的方法可以在 bash 脚本中执行此操作? (除了循环 PATH 的元素并手动进行搜索之外?)
答案1
您始终可以通过以下方式获取第二个路径foo
:
foo=$(type -Pa foo | tail -n+2 | head -n1)
(前提是文件路径不包含换行符)。
请注意,这可能是一个相对路径,在运行后将不再有效cd
。
然后你可以这样做:
hash -p "$foo" foo
所以那 foo
当您运行时被调用foo
。
答案2
我认为没有比枚举PATH
.这并不难。
#!/bin/bash
set -f; IFS=:
for d in $PATH; do
if [[ -f $d/foo && -x $d/foo && ! $d/foo -ef /home/bob/bin/foo ]]; then
exec "$d/foo" "$@"
exit 126
fi
done
echo "$0: foo (real) not found in PATH"
exit 127
我假设 PATH 中没有空条目。空的 PATH 条目是邪恶的,请.
显式编写(或者最好根本不包含它)。
如果您只foo
从命令行运行而不是从其他程序运行,请将其设为函数而不是脚本。从函数中,运行command foo
以隐藏该函数。
答案3
如果 foo 很重要,那么应该在脚本中配置它。我认为这是一个很好的做法,因为您可以将其明确地与脚本挂钩。我不喜欢间接、隐式或隐藏的东西……如果要部署脚本,它们应该非常简单。
否则,如果你坚持要找到它,那么可以通过以下方式找到它:
whereisfoo="$(which foo)"
如果这还不够,那么我担心您所做的事情可能太复杂了。
答案4
我认为这是一种比吉尔斯的答案稍微不那么混乱的方法(尽管我承认这是一个主观判断)。
#!/bin/sh this_dir=$(dirname "$0") # 或者,将其硬编码为 this_dir=$HOME/bin redacted_PATH=$(echo ":${PATH}:" | sed -e "s:\:$this_dir\::\::" -e "s/^://" -e 's/:$// ') if obliged_prog=$(PATH=$redacted_PATH which foo) 然后 ⋮ # 运行 /usr/bin/foo 之前需要做的事情 “$(obscured_prog)”论点 # 可能是“$@”,但也可能不是。 ⋮ # 运行 /usr/bin/foo 后需要做的事情 别的 echo "$0: foo (real) 在 PATH 中找不到。" ⋮ # 任何你想做的代码。 菲
这将构造redacted_PATH
为$PATH
减去$HOME/bin
此私有副本所在的目录foo
。
echo ":${PATH}:"
在 的开头和结尾添加冒号$PATH
,因此它的每个组成部分的前面和后面都会有一个冒号 - 即使是第一个和最后一个。搜索sed
次数为
: $this_dir :
(为“清晰”添加空格)并将其替换为
:
即,它$this_dir
从 中删除":${PATH}:"
。然后它删除开头和结尾的冒号。
然后我们暂时设置PATH
为$redacted_PATH
并搜索foo
使用which
。如果成功,我们将获得它的完整路径(例如,/bin/foo
或/usr/bin/foo
),我们用它来运行 的真实(公共/共享/系统)副本foo
。由于我们PATH
只是暂时更改,
/bin/foo
因此可以访问用户的环境$PATH
,因此,如果/bin/foo
运行brillig
,它可以找到$HOME/bin/brillig
(如果存在)。
$HOME/bin
如果多次出现就会出现问题$PATH
,但这并不难解决。