检测 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 特定的功能)。
它包括将相对路径转换为绝对路径的功能。它为此使用readlink
或realpath
实用程序,但如果提供的目录没有..
或其他链接作为其路径的组成部分,则不需要这些工具。除此之外,该函数不需要外壳外部的任何程序。
# 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
中删除重复和尾随/
:
现在假设$d
包含您要检查的目录。然后通过管道检查前面的命令以检查$PATH
中的$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 中是不是有特定的目录条目的主要内容,如果未能解决你的问题,请参考以下文章