在Windows上编写批处理或者在Linux上编写shell脚本时,经常会有需要判断脚本中依赖的某个命令是否存在的需求,根据所依赖的命令是否存在,我们可能会需要执行不同的代码路径,或者报错退出。
  在上,我们可以使用命令来进行判断,在上,我们则有,,,等命令可以使用。
  Windowscmd.exe
  在Windows的命令提示符cmd.exe下,我们可以使用where命令来检测当前目录或%PATH%下,有没有我们所需要的命令。如:
  wherenotepad
  whereslmgr.vbs
  如果找到匹配的话,%ERRORLEVEL%会被设为0,否则%ERRORLEVEL%则会被设为1。
  where命令后所跟的名字可以不包含后缀,也可以包含后缀(如.exe,.bat,.cmd等),如果不包含后缀,where命令会自动补全%PATHEXT%里列出的后缀并进行查找。
  需要注意的是,如果没有指明后缀,如wheretest,且在当前目录或%PATH%下存在同名无后缀的文件,也是会被where所匹配到的,然而由于缺少后缀,该文件却是不可执行的。也是说,存在where命令正常执行,返回匹配结果,却不存在相应的命令的情况。
  另外,where命令在查找匹配时,会按照以下规则进行:
  1、如果在多个文件夹下存在匹配,则按照先当前文件夹,再依照%PATH%中指定的顺序进行排序;
  2、如果同一文件夹下有多个匹配,则会按文件名进行排序。
  如在C:Userszzzbuzz目录下执行whereabc:
  C:Userszzzbuzzabc
  C:Userszzzbuzzabc.bat
  C:Userszzzbuzzabc.cmd
  C:Userszzzbuzzabc.exe
  C:Windowsabc
  C:Windowsabc.bat
  C:Windowsabc.cmd
  C:Windowsabc.exe
  而在执行一个可执行文件时,则是按照另外的规则:
  如果在多个文件夹下存在匹配,则按照先当前文件夹,再依照%PATH%中指定的顺序进行选择;
  如果同一文件夹下有多个匹配,则会依照%PATHEXT%中列出的优先级,进行选择,没有后缀的文件则永远不可能被执行。
  如%PATHEXT%的值为.COM;.EXE;.BAT;.CMD,则在输入abc时,上例中的C:Userszzzbuzzabc.exe会被执行。
  由于使用where命令查找可执行文件与实际执行可执行文件时的优先级规则不同,因此其实我们并不能保证where命令成功执行时一定有相应的命令存在,也不能保证where命令返回的结果中排在前面的命令会被优先执行。这一点在考虑边界情况时需要注意。
  Linux
  在Linux下,我们有,,,等命令可以用于检测命令可用性。虽然初看之下选择很多,但是考虑到跨平台兼容性,并非这其中的所有命令都是合适的选择。
  which
  which是一个可以返回可执行命令完整路径的命令,在各发行版中一般由which包提供(在Debian系中由debianutils包提供)。当相应命令存在时,会打印其在系统上的位置;而不存在时,根据系统不同,可能不会有任何输出,也可能会有相应的提示信息(如在Cygwin下,会有类似如下输出which:nocmdin(/usr/local/bin:/usr/bin:/bin))。
  命令用法如下:
  $whichbash
  /usr/bin/bash
  $echo$?
  0
  $whichnonexistent
  $echo$?
  1
  脚本写法如下:
  CMD=cmd
  ifwhich"$CMD">/dev/null2>&1;then
  echo命令$CMD存在
  else
  echo命令$CMD不存在
  fi
  不过在脚本中使用which来检测命令并非一个好的选择,因为
  · which是一个外部命令,在不同系统中的行为并非总是一致的,甚至在一些系统中,无论是否有匹配的命令,其返回值永远为0;
  · 有些系统会对which命令挂钩,在调用which命令时,可能还会有些额外的代码被执行。
  type
  type是shell内置的命令,可以用于显示命令的类型,当然同样也可以用于检测命令是否存在。存在时会在STDOUT输出相关信息,并返回0;否则会在STDERR中打印相关错误信息,并返回大于0的值。
  由于type命令是用于显示命令类型的,自然也会匹配到shell中定义的alias,function,builtin等非外部命令的名字,因此我们可以附上-P选项来仅匹配外部命令。不过需要注意的是-P选项并没有定义在POSIX标准中,使用时需要考虑到潜在的兼容性问题。
  命令用法如下:
  $typebash
  bashis/usr/bin/bash
  $echo$?
  0
  $typenonexistent
  nonexistent:notfound
  $echo$?
  1
  脚本写法如下:
  CMD=cmd
  iftype"$CMD">/dev/null2>&1;then
  echo命令$CMD存在
  else
  echo命令$CMD不存在
  fi
  hash
  hash是shell内置的可以用于缓存命令在磁盘上位置的命令,成功执行时没有任何输出,返回0;而执行失败时,则会在STDERR上打印相应的提示信息,并返回一个大于0的值。
  命令用法如下:
  $hashbash
  $echo$?
  0
  $hashnonexistent
  sh:1:hash:nonexistent:notfound
  $echo$?
  1
  脚本写法如下:
  CMD=cmd
  ifhash"$CMD"2>/dev/null;then
  echo命令$CMD存在
  else
  echo命令$CMD不存在
  fi
  使用hash命令来检测命令可用性有一额外的副作用是会将被检测的命令缓存在shell的hash表中以减少后续定位该命令所需的时间。因此,如果被检测的命令随后需要被执行的话,这一副作用正好是我们所需要的了;反之,则好不要使用hash以避免引入该副作用。
  command
  command同样也是shell内置的命令,可以用于执行命令,或者使用-v选项以显示关于命令的有关信息。
  该命令的STDERR用于输出可能的诊断信息;指定-v选项时,命令的STDOUT会被用于显示命令的路径。
  当能够找到相关匹配的命令时,command返回0,否则会返回大于一个0的值。
  命令用法如下:
  $command-vbash
  /usr/bin/bash
  $echo$?
  0
  $command-vnonexistent
  $echo$?
  1
  脚本写法如下:
  CMD=cmd
  ifcommand-v"$CMD">/dev/null2>&1;then
  echo命令$CMD存在
  else
  echo命令$CMD不存在
  fi
  command的行为被POSIX标准良好地定义了,在各系统中有着一致的行为,是编写脚本时作为检测命令可用性的一个良好选择。