检测 PATH 中是不是有特定的目录条目

Posted

技术标签:

【中文标题】检测 PATH 中是不是有特定的目录条目【英文标题】:Detect if PATH has a specific directory entry in it检测 PATH 中是否有特定的目录条目 【发布时间】:2010-11-26 15:12:24 【问题描述】:

使用/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

【问题讨论】:

是否应该将接受的答案更改为来自@Gordon Davidson 的高度投票、基于 bash 的内置答案? 【参考方案1】:

使用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 的扩展和要搜索的路径之前添加冒号可以解决子字符串匹配问题;双引号路径避免了元字符的麻烦。

【讨论】:

无需在[[]] 内引用。那里不会发生分词和路径名扩展。 @bobbogo 在= 的右侧需要引号,以防目录包含任何 glob 元字符(这很不寻常,但可能)。它对左侧没有影响,但我更喜欢在任何地方都用双引号括起来(除非有特定的理由不这样做),而不是试图跟踪所有关于将它们关闭的安全或不安全的奇怪规则(例如此处比较的右侧)。 @GordonDavisson 你是对的!出现在==(等人)的 RHS 扩展中的任何 glob 模式都被解释为这样。天哪,你每天都在 Stack 上学到一些东西。 TVM 我相信如果$HOME/bin$PATH 中的第一个或最后一个路径,这将失败 @MestreLion 即使那样它也应该可以正常工作;这就是为什么需要 ":$PATH:" 中的额外冒号的原因。【参考方案2】:

这里是没有grep的方法:

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

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

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

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

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

【讨论】:

?(:*) 是一个 extended glob 模式,需要在大多数平台上单独显式启用 (shopt -s extglob)。【参考方案3】:

绝对没有必要为此使用像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

【讨论】:

如果您有可以从 sh、ksh、bash 或 zsh 获取的文件,此方法很有帮助【参考方案4】:

一些非常简单和天真的事情:

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

无论您在哪里搜索。您可以将$? 放入变量中或使用正确的if 语句来代替&&

限制包括:

上面将匹配较大路径的子字符串(尝试匹配“bin”,它可能会找到它,尽管“bin”不在您的路径中,/bin 和 /usr/bin 是)李> 上面不会自动展开像~这样的快捷方式

或者使用 perl 单行:

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

它仍然有一个限制,它不会进行任何 shell 扩展,但如果子字符串匹配,它不会失败。 (以上匹配“/usr/bin”,以防不清楚)。

【讨论】:

在 JasonWoof 的回答中匹配行的开头/结尾和冒号可以解决您的子字符串匹配限制。波浪号扩展不是一个常见问题,因为大多数人允许在分配时扩展它(例如PATH="~/bin:$PATH"),但如果需要,可以通过替换来处理。 我正在使用 grep 解决方案,用冒号包裹模式。 受您的回答启发: if [ "$(readlink -f /etc/mtab | grep '\/proc\/'&& echo "found it")" ];然后 echo 做其他事情 echo do something different fi 是你提到的 if 的正确用法吗?【参考方案5】:

这是一个纯 bash 实现,不会因部分匹配而出现误报。

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

这里发生了什么? =~ 运算符使用从 3.0 版开始的 bash 中存在的正则表达式模式支持。正在检查三种模式,由正则表达式的 OR 运算符 | 分隔。

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

在正则表达式中,^ 匹配行首,$ 匹配行尾。如所写,第一个模式只有在它正在寻找的路径是 $PATH 中的第一个值时才会评估为真。如果它正在寻找的路径是 $PATH 中的最后一个值,第三个模式才会评估为 true。当第二个模式在其他值之间找到它正在寻找的路径时,它将评估为 true,因为它正在寻找 $PATH 变量使用的分隔符 :,到正在搜索的路径的任一侧。

【讨论】:

【参考方案6】:

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

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

# 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。

【讨论】:

【参考方案7】:

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

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

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

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

回声 $PATH | tr -s / | sed 's/\/:/:/g;s/:/\n/g' | grep -q "^$d$" ||回声“缺少$d”

【讨论】:

【参考方案8】:

这是一种蛮力方法,但它适用于所有情况,除非路径条目包含冒号。并且没有使用除 shell 之外的任何程序。

previous_IFS=$IFS
dir_in_path='no'
export IFS=":"
for p in $PATH
do
  [ "$p" = "/path/to/check" ] && dir_in_path='yes'
done

[ "$dir_in_path" = "no" ] && export PATH="$PATH:/path/to/check"
export IFS=$previous_IFS

【讨论】:

以上是关于检测 PATH 中是不是有特定的目录条目的主要内容,如果未能解决你的问题,请参考以下文章

检测是不是昨天创建的目录

删除特定 RawContact 内容目录条目

如何使用Python查找目录中是不是有任何txt文件

c#中如何检测文件路径是不是存在

python操作文件与目录

Crontab - 在目录中运行