如何查找Linux命令是否存在

Posted by KC on September 25, 2015

目录:

1. 起因

最近在做发布流程。

初期的项目得不到有效的流程保证是常有的事情。但大部分情况下,这不是技术所能解决的。自动化系统发布过程只是流程化/规范化产品开发过程的其中一个方面,但也是最重要的方面之一。

做发布脚本的时候新申请来的机器总是没有得到必要的初始化,并且每台机器的环境可能都会有些不同,所以需要做一些(一次性的)检查,看看哪些命令没有装上,或者哪些端口/服务没有打开。

2. 分析

whereiswhich都可以显示命令所在的路径,正常情况下,如果不存在,不会有任何输出。但我发现whereis有时候不一定能得到你想要的返回,比如在 OS X (基于10.10 Yosemite测试) 就不会正确地设置退出状态,不论命令是否存在,echo $? 都会返回状态 0 ,另外也有说法表示 which 命令在某些操作系统下也会有同样问题。而且有一些操作系统还会更改它的输出(CentOS 6下我就发现输出和Ubuntu可能有区别),甚至是挂上和包管理器相关的一些操作上1

所以,尽量不要使用这两个命令。可以尝试以下命令取而代之:

1
2
3
$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

PS: 有人会建议使用 2>&- 来替代 2>/dev/null,原因是前者更短更简洁。但实际上这两种写法的效果并非完全一样:2>&- 用于关闭FD2即stderr,当程序尝试写入stderr的时候它可能会导致错误。2

但是当我们使用 /bin/sh 作为shell的时候要注意了,POSIX并没有非常明确地定义 typehash 命令的退出时候的状态码。比如 hash 可能会在所要查找的命令本身并不存在的情况下也成功退出。所以请尽量使用 command 命令,它在POSIX中的退出行为被清晰地定义过。

如果我们用的是 Bash shell,typehash 就是可以非常放心地使用了。type 在bash中有一个 -P 参数用于仅在PATH下搜索命令,而 hash 使用后还会将命令的路径进行哈希索引(这样下次你用这个命令的时候会更快一些)。

以下是一个使用gdate的例子:

1
2
3
4
5
6
7
gnudate() {
   if hash gdate 2>/dev/null; then
       gdate "$@"
   else
       date "$@"
   fi
}

3. 总结

  • 使用Bash作为shell的时候,推荐使用 hash(下次用该命令时更快)或者 type(内建命令)

  • 写POSIX脚本的时候,请使用 command -v

4. 参考