我想要一种在系统范围内或为单个用户向$ PATH添加内容的方法,而不必多次添加相同的路径。
想要这样做的一个原因是,可以在中添加内容.bashrc
,而无需登录,并且在使用(例如)的系统中更有用lightdm
,该系统从不调用.profile
。
我想要一种在系统范围内或为单个用户向$ PATH添加内容的方法,而不必多次添加相同的路径。
想要这样做的一个原因是,可以在中添加内容.bashrc
,而无需登录,并且在使用(例如)的系统中更有用lightdm
,该系统从不调用.profile
。
Answers:
假设我们要添加的新路径是:
new=/opt/bin
然后,使用任何POSIX shell,我们可以进行测试以查看new
路径中是否已经存在,如果没有,则添加它:
case ":${PATH:=$new}:" in
*:"$new":*) ;;
*) PATH="$new:$PATH" ;;
esac
注意冒号的使用。如果没有冒号,我们可能会认为它new=/bin
已经在路径中,因为它的模式与on匹配/usr/bin
。尽管PATH通常包含许多元素,但PATH中零和一元素的特殊情况也将得到处理。的路径最初不具有元素(即空的)的情况下,通过使用处理的${PATH:=$new}
哪个受让人PATH
到$new
,如果它是空的。以这种方式设置参数的默认值是所有POSIX Shell的功能:请参阅POSIX文档的 2.6.2节。)
为了方便起见,可以将以上代码放入一个函数中。可以在命令行中定义此函数,或者要使其永久可用,可以将其放入外壳程序的初始化脚本中(对于bash用户,应为~/.bashrc
):
pupdate() { case ":${PATH:=$1}:" in *:"$1":*) ;; *) PATH="$1:$PATH" ;; esac; }
要使用此路径更新功能将目录添加到当前PATH:
pupdate /new/path
PATH
为空,这将为添加一个空条目(即当前目录)PATH
。我认为您还需要另一种情况。
case
。做吧case "${PATH:=$new}"
。请参阅我自己的答案以获取类似的后备广告。
创建一个/etc/profile.d
名为的文件,例如mypath.sh
(或您想要的任何文件)。如果您使用的是lightdm,请确保它是可行的,否则请使用/etc/bashrc
或来自该文件的文件。此外,还具有以下功能:
checkPath () {
case ":$PATH:" in
*":$1:"*) return 1
;;
esac
return 0;
}
# Prepend to $PATH
prependToPath () {
for a; do
checkPath $a
if [ $? -eq 0 ]; then
PATH=$a:$PATH
fi
done
export PATH
}
# Append to $PATH
appendToPath () {
for a; do
checkPath $a
if [ $? -eq 0 ]; then
PATH=$PATH:$a
fi
done
export PATH
}
$ PATH开头(前面)的内容优先于后面的内容,反之,结尾(后面)的内容将被前面的内容取代。这意味着如果$ PATH是/usr/local/bin:/usr/bin
并且gotcha
两个目录中都有一个可执行文件,则其中一个/usr/local/bin
默认情况下将使用。
现在,您可以-在同一文件,另一个Shell配置文件或命令行中-使用:
appendToPath /some/path /another/path
prependToPath /some/path /yet/another/path
如果位于中.bashrc
,则在启动新Shell时将防止该值出现多次。有一个局限性,如果您想添加预先添加的内容(即在$ PATH中移动路径),反之亦然,则必须自己完成。
$PATH
与分开IFS=:
最终比更加灵活case
。
case
IMO 的一种骇客用途。我想awk
在这里也可以很好地利用它。
gawk
可以直接分配$PATH
。
您可以这样操作:
echo $PATH | grep /my/bin >/dev/null || PATH=$PATH:/my/bin
注意:如果从其他变量构建PATH,请检查它们是否不为空,因为许多shell会将“”解释为“”。。
-q
POSIX需要grep 的手册页,但是我不知道这是否意味着仍然有一些(非POSIX)不具备此功能。
/my/bin
该代码的重要部分是检查是否PATH
包含特定路径:
printf '%s' ":${PATH}:" | grep -Fq ":${my_path}:"
也就是说,保证在每个路径PATH
上分隔两由双方PATH
分隔符(:
),然后检查(-q
)是否为文字字符串(-F
)组成的的PATH
分离,你的路径,另一个PATH
分离器中存在。如果没有,则可以安全地添加路径:
if ! printf '%s' ":${PATH-}:" | grep -Fq ":${my_path-}:"
then
PATH="${PATH-}:${my_path-}"
fi
这应该与POSIX兼容,并且应该与任何不包含换行符的路径一起使用。如果希望它在与POSIX兼容的情况下与包含换行符的路径一起使用,则更为复杂,但是如果您具有grep
支持的支持-z
,则可以使用它。
多年来,我一直在各种~/.profile
文件中随身携带这个小功能。我认为它是由系统管理员在我曾经工作过的实验室中编写的,但我不确定。无论如何,它与Goldilock的方法类似,但略有不同:
pathmunge () {
if ! echo $PATH | /bin/grep -Eq "(^|:)$1($|:)" ; then
if [ "$2" = "after" ] ; then
PATH=$PATH:$1
else
PATH=$1:$PATH
fi
fi
}
因此,要将新目录添加到的开头PATH
:
pathmunge /new/path
到最后:
pathmunge /new/path after
/bin/grep
->grep
我注意到您自己的答案有一个单独的函数,分别用于将追加或添加到$PATH
。我喜欢这个主意。因此,我添加了一些参数处理。我还正确_
命名了它:
_path_assign() { oFS=$IFS ; IFS=: ; add=$* ; unset P A ; A=
set -- ${PATH:=$1} ; for p in $add ; do {
[ -z "${p%-[AP]}" ] && { unset P A
eval ${p#-}= ; continue ; }
for d ; do [ -z "${d%"$p"}" ] && break
done ; } || set -- ${P+$p} $* ${A+$p}
done ; export PATH="$*" ; IFS=$oFS
}
% PATH=/usr/bin:/usr/yes/bin
% _path_assign \
/usr/bin \
/usr/yes/bin \
/usr/bin/nope \
-P \
/usr/nope/bin \
/usr/bin \
-A \
/nope/usr/bin \
/usr/nope/bin
% echo $PATH
/usr/nope/bin:/usr/bin:/usr/yes/bin:/usr/bin/nope:/nope/usr/bin
默认情况下,它会-A
附加到$PATH
,但是您可以-P
通过-P
在参数列表中的任意位置添加此行为来使其重复。您可以再次-A
递给它,使其切换回挂起状态-A
。
在大多数情况下,我建议人们避免使用eval
。但是,我认为这是其永久使用的一个典范。在这种情况下,唯一eval
可以看到的语句是P=
或A=
。它的参数值在调用之前就经过严格测试。这是为了什么eval
。
assign() { oFS=$IFS ; IFS=: ; add=$*
set -- ${PATH:=$1} ; for p in $add ; do {
for d ; do [ -z "${d%"$p"}" ] && break
done ; } || set -- $* $p ; done
PATH="$*" ; IFS=$oFS
}
这将接受您提供的任意数量的参数,$PATH
并且仅在参数尚未添加时才将其添加一次$PATH
。它仅使用完全可移植的POSIX shell脚本,仅依赖于shell内置,并且速度非常快。
% PATH=/usr/bin:/usr/yes/bin
% assign \
/usr/bin \
/usr/yes/bin \
/usr/nope/bin \
/usr/bin \
/nope/usr/bin \
/usr/nope/bin
% echo "$PATH"
> /usr/bin:/usr/yes/bin:/usr/nope/bin:/nope/usr/bin
_
前缀为shell函数使它们成为“适当命名空间”的想法从何而来?在其他语言中,它通常表示一个内部全局函数(即,需要是全局函数,但不打算在外部用作API的一部分)。我的名字当然不是一个好选择,但是在我看来,仅使用_
它根本无法解决冲突问题-例如,使用一个实际的名称空间会更好。mikeserv_path_assign()
。
_
则需要切换软件包管理器。无论如何,从本质上讲,这只是一个“全局的,内部的函数” -它对于从声明它的shell调用的每个shell都是全局的,并且它只是解释器的内存中悬挂的一点解释语言脚本。unix.stackexchange.com/questions/120528/…–
unset a
(或等效)在个人资料末尾?
看哪!工业强度的12行...技术上 bash-和zsh的便携外壳功能悉心爱你的~/.bashrc
或~/.zshrc
选择的启动脚本:
# void +path.append(str dirname, ...)
#
# Append each passed existing directory to the current user's ${PATH} in a
# safe manner silently ignoring:
#
# * Relative directories (i.e., *NOT* prefixed by the directory separator).
# * Duplicate directories (i.e., already listed in the current ${PATH}).
# * Nonextant directories.
+path.append() {
# For each passed dirname...
local dirname
for dirname; do
# Strip the trailing directory separator if any from this dirname,
# reducing this dirname to the canonical form expected by the
# test for uniqueness performed below.
dirname="${dirname%/}"
# If this dirname is either relative, duplicate, or nonextant, then
# silently ignore this dirname and continue to the next. Note that the
# extancy test is the least performant test and hence deferred.
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
# Else, this is an existing absolute unique dirname. In this case,
# append this dirname to the current ${PATH}.
PATH="${PATH}:${dirname}"
done
# Strip an erroneously leading delimiter from the current ${PATH} if any,
# a common edge case when the initial ${PATH} is the empty string.
PATH="${PATH#:}"
# Export the current ${PATH} to subprocesses. Although system-wide scripts
# already export the ${PATH} by default on most systems, "Bother free is
# the way to be."
export PATH
}
为瞬时的荣耀做好准备。然后,不要这样做,而是希望能做到最好:
export PATH=$PATH:~/opt/bin:~/the/black/goat/of/the/woods/with/a/thousand/young
改为这样做,并确保获得最佳,无论您是否真的想要:
+path.append ~/opt/bin ~/the/black/goat/of/the/woods/with/a/thousand/young
安全地添加和添加到当前${PATH}
内容上并不是一件平常的事。虽然方便且看似明智,但这种形式的export PATH=$PATH:~/opt/bin
单线会带来如下恶魔般的并发症:
偶然的相对目录名(例如export PATH=$PATH:opt/bin
)。虽然bash
和zsh
默默承受,大多忽略了相对dirnames中大多数情况下,相对dirnames中的任何前缀h
或t
(和其他可能的恶意字符)原因既可耻残害自己ALA小林正树的开创性1962年的杰作切腹:
# Don't try this at home. You will feel great pain.
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:harakiri && echo $PATH
/usr/local/bin:/usr/bin:arakiri
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:tanuki/yokai && echo $PATH
binanuki/yokai # Congratulations. Your system is now face-up in the gutter.
偶然重复的目录名。尽管重复的${PATH}
dirname在很大程度上是无害的,但它们也是不必要的,繁琐的,效率低下的,阻碍了可调试性并促进了驱动器磨损–有点像这样的答案。虽然NAND式固态硬盘(当然免疫)读取磨损,硬盘都没有。对每个尝试执行的命令进行不必要的文件系统访问都意味着以相同的速度不必要地磨损了读取头。当在嵌套子流程中调用嵌套壳时,重复项特别无害,此时,似乎无害的一线希望像export PATH=$PATH:~/wat
迅速爆炸到${PATH}
地狱第七圈PATH=/usr/local/bin:/usr/bin:/bin:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat
。如果随后在其上附加其他目录名,则只有Beelzebubba可以帮助您。(不要让这种情况发生在您宝贵的孩子身上。)
${PATH}
目录名在很大程度上是无害的,但它们通常也是不需要的,繁琐的,效率低下的,阻碍可调试性并加剧了驱动器的磨损。如此,友好的自动化功能就像上面定义的shell函数一样。我们必须从自己中拯救自己。
为了避免歧义(例如,使用当前命令${PATH}
或系统范围内其他地方定义的外部命令中的外部命令),理想情况下,用户定义的壳函数应以唯一的子字符串作为前缀或后缀,这些子字符串受标准命令基本名支持bash
,zsh
但对于标准命令基本名,则被禁止(例如)+
。
嘿。有用。不要评价我
因为追加到当前${PATH}
要比追加到当前更安全${PATH}
,所以所有事物都是平等的,而从没有过。用特定于用户的命令覆盖系统范围的命令最好是不卫生的,最坏的情况是疯狂的。例如,在Linux下,下游应用程序通常期望使用GNU coreutils命令的变体,而不是自定义的非标准派生或替代。
也就是说,绝对有有效的用例。定义等效+path.prepend()
函数很简单。Sans prolix模糊不清,因为他和她共同的理智:
+path.prepend() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
PATH="${dirname}:${PATH}"
done
PATH="${PATH%:}"
export PATH
}
吉尔 ' 接受的答案在别处是在一般情况下,作为一个令人印象深刻的最佳‘壳不可知幂等追加’。在一般情况下bash
,并zsh
用无不良符号链接,但是,所需要的性能损失做这样伤心的Gentoo ricer的我。即使存在不希望的符号链接,是否add_to_PATH()
值得为每个参数派生一个子shell 是否值得潜在地插入符号链接重复项。
对于要求甚至消除符号链接重复项的严格用例,此zsh
特定于变体的方法是通过高效的内置函数而不是效率低下的fork:
+path.append() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname:A}:"* &&
-d "${dirname}" ]] || continue
PATH="${PATH}:${dirname}"
done
PATH="${PATH#:}"
export PATH
}
请注意,*":${dirname:A}:"*
而不是*":${dirname}:"*
原始的。可悲的是,在大多数其他壳层之下(包括),都缺少:A
一种奇妙的zsh
主义bash
。引用man zshexpn
:
答:像
a
修饰符一样,将文件名转换为绝对路径,然后将结果传递给realpath(3)
库函数以解析符号链接。注:没有一个系统的realpath(3)
库函数,符号链接没有解决,所以这些系统a
和A
是等价的。
别客气。享受安全的炮击。您现在应该得到它。
这是我的函数编程风格的版本。
*PATH
变量,不仅限于PATH
。还值得注意的是:
export
荷兰国际集团; 留给调用者(参见示例)bash
; 没有分叉path_add(){ #$ 1:确保在给定路径字符串中的元素恰好在一次 #$ 2:现有路径字符串值(“ $ PATH”,而不是“ PATH”) #$ 3(可选,任何内容):如果有,则附加$ 1;否则,前置 # # 例子: #$ export PATH = $(path_add'/ opt / bin'“ $ PATH”) #$ CDPATH = $(path_add'/ Music'“ $ CDPATH” at_end) 本地-r已经_present =“(^ |:)$ {1}($ | :)” 如果[[“ $ 2” =〜$ already_present]]; 然后 回声“ $ 2” elif [[$#== 3]]; 然后 回声“ $ {2}:$ {1}” 其他 回声“ $ {1}:$ {2}” 科幻 }
此脚本允许您在末尾添加$PATH
:
PATH=path2; add_to_PATH after path1 path2:path3
echo $PATH
path2:path1:path3
或在以下内容的开头添加$PATH
:
PATH=path2; add_to_PATH before path1 path2:path3
echo $PATH
path1:path3:path2
# Add directories to $PATH iff they're not already there
# Append directories to $PATH by default
# Based on https://unix.stackexchange.com/a/4973/143394
# and https://unix.stackexchange.com/a/217629/143394
add_to_PATH () {
local prepend # Prepend to path if set
local prefix # Temporary prepended path
local IFS # Avoid restoring for added laziness
case $1 in
after) shift;; # Default is to append
before) prepend=true; shift;;
esac
for arg; do
IFS=: # Split argument by path separator
for dir in $arg; do
# Canonicalise symbolic links
dir=$({ cd -- "$dir" && { pwd -P || pwd; } } 2>/dev/null)
if [ -z "$dir" ]; then continue; fi # Skip non-existent directory
case ":$PATH:" in
*":$dir:"*) :;; # skip - already present
*) if [ "$prepend" ]; then
# ${prefix:+$prefix:} will expand to "" if $prefix is empty to avoid
# starting with a ":". Expansion is "$prefix:" if non-empty.
prefix=${prefix+$prefix:}$dir
else
PATH=$PATH:$dir # Append by default
fi;;
esac
done
done
[ "$prepend" ] && [ "$prefix" != "" ] && PATH=$prefix:$PATH
}