bash 检测用户路径中是否有特定目录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1396066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:29:43  来源:igfitidea点击:

Detect if user's path has a specific directory in it

bashshellpath

提问by epochwolf

With /bin/bash, how would I detect if a user has a specific directory in their $PATH variable?

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

For example

例如

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

采纳答案by Adam Batkin

Something really simple and naive:

一些非常简单和幼稚的事情:

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

Where whatever is what you are searching for. Instead of &&you can put $?into a variable or use a proper ifstatement.

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

Limitations include:

限制包括:

  • The above will match substrings of larger paths (try matching on "bin" and it will probably find it, despite the fact that "bin" isn't in your path, /bin and /usr/bin are)
  • The above won't automatically expand shortcuts like ~
  • 以上将匹配较大路径的子字符串(尝试匹配“bin”,它可能会找到它,尽管“bin”不在您的路径中,/bin 和 /usr/bin 是)
  • 以上不会自动展开快捷方式之类的~

Or using a perl one-liner:

或者使用 perl one-liner:

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

That still has the limitation that it won't do any shell expansions, but it doesn't fail if a substring matches. (The above matches "/usr/bin", in case that wasn't clear).

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

回答by Gordon Davisson

Using grepis overkill, and can cause trouble if you're searching for anything that happens to include RE metacharacters. This problem can be solved perfectly well with bash's builtin [[command:

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

Note that adding colons before both the expansion of $PATH and the path to search for solves the substring match issue; double-quoting the path avoids trouble with metacharacters.

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

回答by Paused until further notice.

Here's how to do it without grep:

这是没有的方法grep

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

The key here is to make the colons and wildcards optional using the ?()construct. There shouldn't be any problem with metacharacters in this form, but if you want to include quotes this is where they go:

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

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

This is another way to do it using the match operator (=~) so the syntax is more like grep's:

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

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

回答by tripleee

There is absolutely no need to use external utilities like grepfor this. Here is what I have been using, which should be portable back to even legacy versions of the Bourne shell.

绝对不需要像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

回答by Anthony Geoghegan

I wrote the following shell function to report if a directory is listed in the current PATH. This function is POSIX-compatible and will run in compatible shells such as Dash and Bash (without relying on Bash-specific features).

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

It includes functionality to convert a relative path to an absolute path. It uses the readlinkor realpathutilities for this but these tools are not needed if the supplied directory does not have ..or other links as components of its path. Other than this, the function doesn't require any programs external to the shell.

它包括将相对路径转换为绝对路径的功能。它为此使用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 "" ]; then
    printf "Error: ‘%s' is not a directory.\n" "" >&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 "")"
  elif command -v realpath >/dev/null ; then
    dir="$(realpath "")"
  else
    case "" in
      /*)
        # The path of the provided directory is already absolute.
        dir=""
      ;;
      *)
        # Prepend the path of the current directory.
        dir="$PWD/"
      ;;
    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
}

The part using :$PATH:ensures that the pattern also matches if the desired path is the first or last entry in the PATH. This clever trick is based upon this answer by Glenn Hymanman on Unix & Linux.

使用部分:$PATH:确保了图案也匹配,如果所期望的路径是在所述第一或最后一个条目PATH。这个聪明的技巧是基于Glenn Hymanman 在 Unix & Linux上的这个答案

回答by bmacnaughton

This is a brute force approach but it works in all cases except when a path entry contains a colon. And no programs other than the shell are used.

这是一种蛮力方法,但它适用于所有情况,除非路径条目包含冒号。并且不使用除 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

回答by Jekotia

Here's a pure-bash implementation that will not pick up false-positives due to partial matching.

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

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

What's going on here? The =~ operator uses regex pattern support present in bash starting with version 3.0. Three patterns are being checked, separated by regex's OR operator |.

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

All three sub-patterns are relatively similar, but their differences are important for avoiding partial-matches.

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

In regex, ^matches to the beginning of a line and $matches to the end. As written, the first pattern will only evaluate to true if the path it's looking for is the first value within $PATH. The third pattern will only evaluate to true if if the path it's looking for is the last value within $PATH. The second pattern will evaluate to true when it finds the path it's looking for in-between others values, since it's looking for the delimiter that the $PATH variable uses, :, to either side of the path being searched for.

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

回答by jcr38

$PATHis a list of strings separated by :that describe a list of directories. A directory is a list of strings separated by /. Two different strings may point to the same directory (like $HOMEand ~, or /usr/local/binand /usr/local/bin/). So we must fix the rules of what we want to compare/check. I suggest to compare/check the whole strings, and not physical directories, but remove duplicate and trailing /.

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

First remove duplicate and trailing /from $PATH:

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

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

Now suppose $dcontains the directory you want to check. Then pipe the previous command to check $din $PATH.

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

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