【问题标题】:Detect if executable file is on user's PATH [duplicate]检测可执行文件是否在用户的 PATH [重复]
【发布时间】:2011-09-28 00:12:26
【问题描述】:

在 bash 脚本中,我需要确定名为 foo 的可执行文件是否在 PATH 上。

【问题讨论】:

  • 这是 stackoverflow.com/a/677212 的副本,它有更完整的答案。
  • ........为什么posix中没有这个功能
  • 现有问题不是询问可执行文件是否在用户路径上。它只是询问程序是否存在?所以,我找到了这个问题。不是“重复”。
  • 只需使用 builtin hash 即可。

标签: bash


【解决方案1】:

使用which

$ which myprogram

【讨论】:

  • which 确实很棒 =)
  • 应该避免的,参见stackoverflow.com/a/677212
【解决方案2】:

你可以使用which:

 path_to_executable=$(which name_of_executable)
 if [ -x "$path_to_executable" ] ; then
    echo "It's here: $path_to_executable"
 fi

【讨论】:

  • 检查可执行模式标志是多余的。 which只返回可执行文件(包括脚本)
  • 可执行文件检查的重点是确保其结果是可执行文件,而不是错误消息。
  • 好的,我明白了。 if [ -n "$path_to_executable" ] 会更清楚(更高效)
  • 应该避免的,见stackoverflow.com/a/677212
  • 这实际上是一个糟糕的选择。而是使用内置的替代方案。 (见特雷弗的回答)另外,这个问题与stackoverflow.com/questions/592620/…
【解决方案3】:

您可以使用与 POSIX 兼容的 command 内置函数:

if [ -x "$(command -v "$cmd")" ]; then
    echo "$cmd is in \$PATH"
fi

需要可执行文件检查,因为command -v 检测函数和别名以及可执行文件。

在 Bash 中,您还可以将 type-P 选项一起使用,这会强制进行 PATH 搜索:

if type -P "$cmd" &>/dev/null; then
    echo "$cmd is in \$PATH"
fi

正如 cmets 中已经提到的,请避免使用 which,因为它需要启动外部进程,并且可能会在 some cases 中为您提供不正确的输出。

【讨论】:

    【解决方案4】:

    你也可以使用 Bash 内置 type -P:

    help type
    
    cmd=ls
    [[ $(type -P "$cmd") ]] && echo "$cmd is in PATH"  || 
        { echo "$cmd is NOT in PATH" 1>&2; exit 1; }
    

    【讨论】:

    • 可以简化:type -P "$cmd" && echo "in path" || echo "not in path"
    • 既然他没有指定bash,最好避免bashism stackoverflow.com/a/677212
    • Bash 肯定是在问题中指定的:“在 bash 脚本中……”
    • 出于好奇,为什么不直接这样做:type -P " $cmd" > /dev/null && echo "yay"if type -P "$cmd" > /dev/null; then,而不是使用子shell 并将命令包装在[[ ]] 中?
    • @Hubro 你对浪费fork() 和测试是正确的。请参阅 this answer 以了解 -P 的解释以及在 bashzsh 中均有效的更高效、可重用的函数。
    【解决方案5】:

    如果命令 -v foo ;然后 foo ;否则回显“foo不可用”;菲

    【讨论】:

    【解决方案6】:

    TL;DR:

    bash:

    function is_bin_in_path {
      builtin type -P "$1" &> /dev/null
    }
    

    is_bin_in_path的用法示例:

    % is_bin_in_path ls && echo "found in path" || echo "not in path"
    found in path
    

    zsh:

    请改用whence -p


    对于同时适用于{ba,z}sh 的版本:

    # True if $1 is an executable in $PATH
    # Works in both {ba,z}sh
    function is_bin_in_path {
      if [[ -n $ZSH_VERSION ]]; then
        builtin whence -p "$1" &> /dev/null
      else  # bash:
        builtin type -P "$1" &> /dev/null
      fi
    }
    

    测试所有给定的命令在 $PATH 中都是可执行的:

    # True iff all arguments are executable in $PATH
    function is_bin_in_path {
      if [[ -n $ZSH_VERSION ]]; then
        builtin whence -p "$1" &> /dev/null
      else  # bash:
        builtin type -P "$1" &> /dev/null
      fi
      [[ $? -ne 0 ]] && return 1
      if [[ $# -gt 1 ]]; then
        shift  # We've just checked the first one
        is_bin_in_path "$@"
      fi
    }
    

    示例用法:

    is_bin_in_path ssh-agent ssh-add && setup_ssh_agent
    

    要避免的非解决方案

    这不是一个简短的答案,因为解决方案必须正确处理:

    • 功能
    • 别名
    • 内置命令
    • 保留字

    使用普通type失败的示例(注意type 更改后的标记):

    $ alias foo=ls
    $ type foo && echo "in path" || echo "not in path"
    foo is aliased to `ls'
    in path
    
    $ type type && echo "in path" || echo "not in path"
    type is a shell builtin
    in path
    
    $ type if && echo "in path" || echo "not in path"
    if is a shell keyword
    in path
    

    请注意,在bash 中,which 不是内置的 shell(它在 zsh 中):

    $ PATH=/bin
    $ builtin type which
    which is /bin/which
    

    This answer 说为什么要避免使用which

    避免which。它不仅是一个您启动的外部进程,只需要做很少的事情(意味着像 hashtypecommand 这样的内置程序更便宜),您还可以依靠内置程序来实际做您想做的事情,而外部命令的效果很容易因系统而异。

    为什么要关心?

    • 许多操作系统都有一个which,它甚至不设置退出状态,这意味着if which foo 甚至无法在那里工作,并且会始终报告foo 存在,即使它不存在(请注意,一些 POSIX shell 似乎也为hash 执行此操作)。
    • 许多操作系统让which 执行自定义和恶意操作,例如更改输出,甚至挂钩到包管理器。

    在这种情况下,也要避免command -v

    我刚刚引用的答案建议使用command -v,但这不适用于当前的“$PATH 中的可执行文件吗?”场景:它会失败,就像我上面用简单的type 说明的那样。


    正确的解决方案

    bash中我们需要使用type -P

      -P        force a PATH search for each NAME, even if it is an alias,
    
                builtin, or function, and returns the name of the disk file
                that would be executed
    

    zsh中我们需要使用whence -p

       -p     Do  a  path  search  for  name  even  if  it is an alias,
    
              reserved word, shell function or builtin.
    

    【讨论】:

    • “它适用于别名和函数” - 不正确。在 Bash 中试试这个:alias foo=ls; is_bin_in_path foo && echo "in path" || echo "not in path".
    • 很好,@EugeneYarmash。更新并修复。
    • type -P 是一种 bashism,当在无法保证 shell 是 bash 的脚本中使用时会导致问题 --- 例如,带有 $(shell) 的 makefile。
    • @David Given,这个问题被标记为 bash....
    • @TomHale 原来如此——我的错!
    【解决方案7】:

    我们可以使用which定义一个检查可执行文件是否存在的函数:

    function is_executable() {
        which "$@" &> /dev/null
    }
    

    调用函数就像调用可执行文件一样。 "$@" 确保 which 获得与提供给函数的参数完全相同的参数。

    &> /dev/null 确保 which 写入 stdout 或 stderr 的任何内容都被重定向到空设备(这是一个丢弃写入它的信息的特殊设备),而不是由函数写入 stdout 或 stderr。

    由于函数没有显式返回并返回代码,当它返回时,最新执行的可执行文件的退出代码(在本例中为 which)将是函数的返回代码。如果which 能够找到函数参数指定的可执行文件,则which 将以指示成功的代码退出,否则以指示失败的退出代码退出。 is_executable 会自动复制此行为。

    然后我们可以使用该函数有条件地做某事:

    if is_executable name_of_executable; then
        echo "name_of_executable was found"
    else
        echo "name_of_executable was NOT found"
    fi
    

    这里,if 执行写在它和then 之间的命令——在我们的例子中是is_executable name_of_executable——并根据命令的返回码选择要执行的分支。

    或者,我们可以跳过定义函数,直接在if-语句中使用which

    if which name_of_executable &> /dev/null; then
        echo "name_of_executable was found"
    else
        echo "name_of_executable was NOT found"
    fi
    

    但是,我认为这会降低代码的可读性。

    【讨论】:

    • @RamGhadiyaram 当然,我可以放入一些 cmets。什么是“SO 中的低质量帖子”?
    • 这些是左侧菜单中的第三个审核队列。如果他们认为答案信息很少且没有解释,那么它将进入审核队列。关闭。最常见的代码只有答案会去。在你的情况下是。
    • @RamGhadiyaram 这样好吗?
    猜你喜欢
    • 2013-03-09
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 2012-10-25
    • 1970-01-01
    • 2011-06-14
    相关资源
    最近更新 更多