如何可靠地在PATH上找到程序的完整路径?


12

我需要PATH使用shell脚本找到给定程序的路径。该路径必须是程序的实际完整路径,以后可以传递给其中一个exec*不搜索PATH自身的函数,例如execv

有类似的程序kill,它们既可以作为实际程序使用,又可以内置于外壳中。如果是这种情况,我需要实际程序的完整路径。

PATH2.9.1.1节,POSIX标准的命令搜索和执行中所指定,有许多实用程序可以在上找到程序。

which,这不是任何标准的一部分。在某些系统上,它可以是常规程序,而在某些shell中,它是内置的。它似乎在大多数系统和Shell上都可用,但是具有内置版本的Shell也仅返回内置名称而不是可执行文件的路径。同样,它没有以任何方式标准化,并且可能返回任何输出并采用不同的选项。

bash# which kill
/usr/bin/kill
dash# which kill
/usr/bin/kill
fish# which kill
/usr/bin/kill
mksh# which kill
/usr/bin/kill
tcsh# which kill
kill: shell built-in command.
zsh# which kill
kill: shell built-in command

whence,这是一些shell的内置组件。但是在许多外壳上不可用。它也将返回内置名称而不是程序路径。-p可以将A 传递给wherece以更改此行为。

bash# whence kill
bash: whence: command not found
dash# whence kill
dash: 1: whence: not found
fish# whence kill
fish: Unknown command 'whence'
mksh# whence kill
kill
mksh# whence -p kill
/usr/bin/kill
tcsh# whence kill
whence: Command not found.
zsh# whence kill
kill
zsh# whence -p kill
/usr/bin/kill

commandPOSIX:2008指定内置函数。不幸的是,它还会搜索常规命令和内置程序,并将返回内置程序的名称,而不是由相同名称的内置程序隐藏的程序路径。一些旧的shell尚未实现它。

bash# command -v kill
kill
dash# command -v kill
kill
fish# command -v kill
/usr/bin/kill
mksh# command -v kill
kill
tcsh# command -v kill
command: Command not found.
zsh# command -v kill
kill

我不知道是否enable在POSIX中指定了,但是如果指定了,则可以enable -n which禁用内置的shell which
Muzer

并有realpath
IPOR Sircer

@Muzer在在我手上弹我,enable只提供bashzsh
塞巴斯蒂安·施拉德

1
您需要一个针对特定外壳的可实现方法,该特定外壳正在针对所有外壳运行脚本。脚本不是由随机shell执行的,而是由shebang行中指定的shell执行的。话虽这么说,那将是…… type -p。bash和dash都可以让您说出一个command命令来运行实际的可执行文件,即使有一个函数或内建函数具有相同的名称。
AlexP

1
command正如Q正确指出的那样,@AlexP会跳过函数(和别名),但不会内置函数。而且,您不能总是使用shebang,因为在所有系统上都没有路径可以获取任何给定的Shell,甚至某些POSIX Shell。
dave_thompson_085 '16

Answers:


11

自己搜索即可。

export IFS=":"
[ -z "${1}" ] && exit 1
for dir in $PATH
do if [ -x "${dir}/${1}" ]
   then echo "${dir}/${1}"
        exit 0
   fi
done
echo ${1} not found
exit 1

经测试,在bashdashkshmkshzsh

更新资料

上面的代码很适合用于独立脚本,但是如果您打算将其嵌入到较大的脚本中,则可能需要使用类似以下内容的脚本。

function find_path() {
   IFS_SAVE="${IFS}"
   export IFS=":"
   [ -z "${1}" ] && exit 1
   for dir in $PATH
   do if [ -x "${dir}/${1}" ]
      then echo "${dir}/${1}"
           export IFS="${IFS_SAVE}"
           return 0
      fi
   done
   export IFS="${IFS_SAVE}"
   echo ${1} not found
   return 1
}

这样可以IFS在找到匹配项后将其恢复,也将的替换exitreturn


1
也许是-x而不是-f?
杰夫·谢勒

@JeffSchaller好点,没有理由挑出非可执行文件。
扎卡里·布雷迪

3
如果将其集成到一个较大的Shell脚本中(而不是按照预期的那样单独地将其制作成脚本),则可能要在以后恢复IFS的旧值-否则可能会对IFS产生很多影响脚本的其余部分...
psmears's

为什么要导出IFS变量?在本地shell中设置此设置还不够吗?说到本地,会local IFS便携吗?如果其他方式以相同的方式保存IFS,则以上内容可能无法很好地交互。看着在SE这个问题local可能对于大部分炮弹尽管不是POSIX工作。将原始版本放在(…)子外壳中也可能有效。
MvG
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.