检测用户的路径是否包含特定目录

时间:2009-09-08 20:08:24

标签: bash shell path

使用/bin/bash,我如何检测用户的$ PATH变量中是否有特定目录?

例如

if [ -p "$HOME/bin" ]; then
  echo "Your path is missing ~/bin, you might want to add it."
else
  echo "Your path is correctly set"
fi

8 个答案:

答案 0 :(得分:118)

使用grep是过度的,如果您正在搜索包含RE元字符的任何内容,可能会造成麻烦。使用bash的内置[[命令可以很好地解决这个问题:

if [[ ":$PATH:" == *":$HOME/bin:"* ]]; then
  echo "Your path is correctly set"
else
  echo "Your path is missing ~/bin, you might want to add it."
fi

请注意,在$ PATH扩展和搜索路径之前添加冒号可以解决子字符串匹配问题;双引号路径避免了元字符的麻烦。

答案 1 :(得分:10)

以下是没有grep的方法:

if [[ $PATH == ?(*:)$HOME/bin?(:*) ]]

这里的关键是使用?()构造使冒号和通配符可选。这种形式的元字符不应该有任何问题,但如果你想包含引号,这就是它们的去处:

if [[ "$PATH" == ?(*:)"$HOME/bin"?(:*) ]]

这是另一种使用匹配运算符(=~)的方法,因此语法更像grep

if [[ "$PATH" =~ (^|:)"${HOME}/bin"(:|$) ]]

答案 2 :(得分:5)

一些非常简单和天真的东西:

echo "$PATH"|grep -q whatever && echo "found it"

无论你在寻找什么。您可以将&&放入变量或使用正确的$?语句,而不是if

限制包括:

  • 以上将匹配较大路径的子串(尝试匹配“bin”并且它可能会找到它,尽管“bin”不在您的路径中,/ bin和/ usr / bin都是)
  • 上述内容不会自动扩展〜
  • 等快捷方式

或使用perl one-liner:

perl -e 'exit(!(grep(m{^/usr/bin$},split(":", $ENV{PATH}))) > 0)' && echo "found it"

它仍有限制,它不会进行任何shell扩展,但如果子字符串匹配则不会失败。 (以上情况与“/usr/bin”匹配,如果不清楚的话。)

答案 3 :(得分:4)

绝对不需要像grep那样使用外部实用程序。以下是我一直在使用的内容,它应该可以移植到Bourne shell的旧版本中。

case :$PATH: # notice colons around the value
  in *:$HOME/bin:*) ;; # do nothing, it's there
     *) echo "$HOME/bin not in $PATH" >&2;;
esac

答案 4 :(得分:1)

我编写了以下shell函数来报告目录是否列在当前PATH中。此函数与POSIX兼容,将在兼容的shell(如Dash和Bash)中运行(不依赖于Bash特定的功能)。

它包含将相对路径转换为绝对路径的功能。它使用readlinkrealpath实用程序,但如果提供的目录没有..或其他链接作为其路径的组件,则不需要这些工具。除此之外,该函数不需要shell外部的任何程序。

# Check that the specified directory exists – and is in the PATH.
is_dir_in_path()
{
  if  [ -z "${1:-}" ]; then
    printf "The path to a directory must be provided as an argument.\n" >&2
    return 1
  fi

  # Check that the specified path is a directory that exists.
  if ! [ -d "$1" ]; then
    printf "Error: ‘%s’ is not a directory.\n" "$1" >&2
    return 1
  fi

  # Use absolute path for the directory if a relative path was specified.
  if command -v readlink >/dev/null ; then
    dir="$(readlink -f "$1")"
  elif command -v realpath >/dev/null ; then
    dir="$(realpath "$1")"
  else
    case "$1" in
      /*)
        # The path of the provided directory is already absolute.
        dir="$1"
      ;;
      *)
        # Prepend the path of the current directory.
        dir="$PWD/$1"
      ;;
    esac
    printf "Warning: neither ‘readlink’ nor ‘realpath’ are available.\n"
    printf "Ensure that the specified directory does not contain ‘..’ in its path.\n"
  fi

  # Check that dir is in the user’s PATH.
  case ":$PATH:" in
    *:"$dir":*)
      printf "‘%s’ is in the PATH.\n" "$dir"
      return 0
      ;;
    *)
      printf "‘%s’ is not in the PATH.\n" "$dir"
      return 1
      ;;
  esac
}

如果所需路径是:$PATH:中的第一个或最后一个条目,则使用PATH的部分可确保模式也匹配。这个聪明的技巧基于this answer by Glenn Jackman on Unix & Linux

答案 5 :(得分:1)

这是一个纯bash实现,由于部分匹配而不会出现假阳性。

if [[ $PATH =~ ^/usr/sbin:|:/usr/sbin:|:/usr/sbin$ ]] ; then
  do stuff
fi

这是怎么回事? =〜运算符使用从3.0版开始在bash中提供的regex模式支持。正在检查三种模式,由正则表达式的OR运算符|分隔。

所有三个子模式都相对相似,但是它们的区别对于避免部分匹配很重要。

在正则表达式中,^匹配到行的开头,$匹配到行的结尾。如所写,第一个模式仅在其寻找的路径是$ PATH中的第一个值时才评估为true。如果要查找的路径是$ PATH中的最后一个值,则第三个模式将评估为true。当第二种模式在其他值之间找到它要寻找的路径时,它将评估为true,因为它正在寻找$ PATH变量使用的定界符:到要搜索的路径的任一侧。

答案 6 :(得分:0)

$PATH是由:分隔的字符串列表,用于描述目录列表。目录是由/分隔的字符串列表。两个不同的字符串可能指向同一目录(例如$HOME~,或/usr/local/bin/usr/local/bin/)。所以我们必须修改我们想要比较/检查的规则。我建议比较/检查整个字符串,而不是物理目录,但删除重复和尾随/

首先从/删除重复和尾随$PATH

echo $PATH | tr -s / | sed 's/\/:/:/g;s/:/\n/g'

现在假设$d包含您要检查的目录。然后通过管道上一个命令检查$d中的$PATH

echo $PATH | tr -s / | sed 's/\/:/:/g;s/:/\n/g' | grep -q "^$d$" || echo "missing $d"

答案 7 :(得分:0)

这是一种蛮力方法,但它在所有情况下都有效,除非路径条目包含冒号。除了外壳,没有其他程序被使用。

~/.ssh/known_hosts