在 Bash 中从 $PATH 变量中删除路径的最优雅的方法是什么?

或者更一般地说,如何从 Bash 环境变量的冒号分隔列表中删除某个项目?

几年前,我想我已经看到了一种简单的方法,使用更高级的 Bash 变量展开形式,但如果是这样的话,我已经失去了它的踪迹。在谷歌上快速搜索一下,出人意料地几乎没有相关的结果,也没有我称之为“简单”或“优雅”的结果。例如,分别使用 sed 和 awk 的两个方法:

PATH=$(echo $PATH | sed -e 's;:\?/home/user/bin;;' -e 's;/home/user/bin:\?;;')
PATH=!(awk -F: '{for(i=1;i<=NF;i++){if(!($i in a)){a[$i];printf s$i;s=":"}}}'<<<$PATH)

是否没有直接的东西存在? 是否有类似于 Bash 中的 split ()函数?

更新:
看起来我需要为我故意模糊的问题道歉; 我对解决特定用例的兴趣不如对引发良好讨论的兴趣。幸运的是,我拿到了!

这里有一些非常聪明的技巧。最后,我在工具箱中添加了以下三个函数。奇迹发生在 path _ remove 中,这主要是基于 Martin York 对 awk的 RS 变量的巧妙使用。

path_append ()  { path_remove $1; export PATH="$PATH:$1"; }
path_prepend () { path_remove $1; export PATH="$1:$PATH"; }
path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; }

唯一真正的障碍是使用 sed去除尾随的结肠。不过,考虑到 Martin 剩下的解决方案是多么简单,我还是很愿意接受它!


相关问题: 如何操作 shell 脚本中的 $PATH 元素

77583 次浏览

在 bash 中,由于它支持正则表达式,我只需要:

PATH=${PATH/:\/home\/user\/bin/}

我的肮脏伎俩:

echo ${PATH} > t1
vi t1
export PATH=$(cat t1)

我确实对这个 给你写了一个答案(也使用 awk)。但我不确定这是你想要的?至少在我看来,它的作用很明显,而不是试图融入一行。但是,对于一个简单的一行程序,我建议它只删除一些内容

echo $PATH | tr ':' '\n' | awk '$0 != "/bin"' | paste -sd:

替换是

echo $PATH | tr ':' '\n' |
awk '$0 != "/bin"; $0 == "/bin" { print "/bar" }' | paste -sd:

或(较短但不易读)

echo $PATH | tr ':' '\n' | awk '$0 == "/bin" { print "/bar"; next } 1' | paste -sd:

无论如何,对于同样的问题,以及许多有用的答案,请参阅 给你

由于这个问题相当严重,因为没有优雅的方法,我建议通过重新安排解决方案来避免这个问题: 构建 PATH,而不是试图将其拆除。

如果我知道你真正的问题背景,我可以说得更具体一点。在此期间,我将使用软件构建作为上下文。

软件构建的一个常见问题是,它在某些计算机上出现故障,最终是由于某些人如何配置了他们的默认 shell (PATH 和其他环境变量)。优雅的解决方案是通过完全指定 shell 环境使构建脚本免疫。编写生成脚本,根据您控制的组装部件(例如编译器、库、工具、组件等的位置)来设置 PATH 和其他环境变量。使每个可配置项成为您可以单独设置、验证,然后在脚本中适当使用的项。

例如,我从新雇主那里继承了一个基于 Maven、面向 WebLogic 的 Java 构建。构建脚本因为易碎而臭名昭著,我和另一个新员工花了三个星期(不是全职,只是偶尔,但仍然有很多小时)让它在我们的机器上工作。一个重要的步骤是我控制 PATH,这样我就能准确地知道哪个 Java、哪个 Maven 和哪个 WebLogic 正在被调用。我创建了环境变量来指向这些工具中的每一个,然后基于这些工具加上其他一些工具计算 PATH。类似的技术驯服了其他的可配置设置,直到我们最终创建了一个可重复的构建。

顺便说一下,不要使用 Maven,Java 是可以的,只有在你绝对需要集群的时候才购买 WebLogic (除此之外不需要,特别是不需要它的专有特性)。

祝你好运。

一分钟的尴尬:

# Strip all paths with SDE in them.
#
export PATH=`echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}'`

编辑: 它对以下评论的回应:

$ export a="/a/b/c/d/e:/a/b/c/d/g/k/i:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i"
$ echo ${a}
/a/b/c/d/e:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i


## Remove multiple (any directory with a: all of them)
$ echo ${a} | awk -v RS=: -v ORS=: '/a/ {next} {print}'
## Works fine all removed


## Remove multiple including last two: (any directory with g)
$ echo ${a} | awk -v RS=: -v ORS=: '/g/ {next} {print}'
/a/b/c/d/e:/a/b/c/d/f:
## Works fine: Again!

针对安全问题进行编辑: (与问题无关)

export PATH=$(echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}' | sed 's/:*$//')

这将删除通过删除最后一个条目留下的所有尾冒号,这将有效地将 .添加到您的路径中。

和@litb 一样,我为问题“ 如何操作 shell 脚本中的 $PATH 元素”贡献了一个答案,所以我的主要答案就在那里。

bash和其他 Bourne shell 衍生物中的“分割”功能是通过字段间分隔符 $IFS实现的。例如,要将位置参数($1$2、 ...)设置为 PATH 的元素,请使用:

set -- $(IFS=":"; echo "$PATH")

只要 $PATH 中没有空格,就可以正常工作。使其适用于包含空间的路径元素是一个非常重要的练习——留给感兴趣的读者。使用像 Perl 这样的脚本语言来处理它可能更简单。

我还有一个脚本 clnpath,我经常使用它来设置 PATH。我把它记录在“ 如何防止在 csh 中复制 PATH 变量”的答案中。

这是我能想到的最简单的解决办法:

#!/bin/bash
IFS=:
# convert it to an array
t=($PATH)
unset IFS
# perform any array operations to remove elements from the array
t=(${t[@]%%*usr*})
IFS=:
# output the new array
echo "${t[*]}"

上面的示例将删除 $PATH 中包含“ usr”的任何元素。您可以将“ * usr *”替换为“/home/user/bin”以删除该元素。

更新 per < a href = “ https://stackoverflow. com/users/1127485/sschuberth”> sschuberth

尽管我认为 $PATH中的空间是 太可怕了的想法,但这里有一个解决方案:

PATH=$(IFS=':';t=($PATH);n=${#t[*]};a=();for ((i=0;i<n;i++)); do p="${t[i]%%*usr*}"; [ "${p}" ] && a[i]="${p}"; done;echo "${a[*]}");

或者

IFS=':'
t=($PATH)
n=${#t[*]}
a=()
for ((i=0;i<n;i++)); do
p="${t[i]%%*usr*}"
[ "${p}" ] && a[i]="${p}"
done
echo "${a[*]}"

使这个问题恼人的是第一个和最后一个元素之间的 fencepost 情况。这个问题可以通过改变 IFS 和使用数组来优雅地解决,但是我不知道如何在路径转换为数组形式后重新引入冒号。

下面是一个稍微不那么优雅的版本,它只使用字符串操作从 $PATH中删除一个目录。我已经测试过了。

#!/bin/bash
#
#   remove_from_path dirname
#
#   removes $1 from user's $PATH


if [ $# -ne 1 ]; then
echo "Usage: $0 pathname" 1>&2; exit 1;
fi


delendum="$1"
NEWPATH=
xxx="$IFS"
IFS=":"
for i in $PATH ; do
IFS="$xxx"
case "$i" in
"$delendum") ;; # do nothing
*) [ -z "$NEWPATH" ] && NEWPATH="$i" || NEWPATH="$NEWPATH:$i" ;;
esac
done


PATH="$NEWPATH"
echo "$PATH"

下面是 Perl 的一句俏皮话:

PATH=`perl -e '$a=shift;$_=$ENV{PATH};s#:$a(:)|^$a:|:$a$#$1#;print' /home/usr/bin`

$a变量获取要删除的路径。s(替代)和 print命令隐式地操作 $_变量。

好东西。 我用这个来防止被骗。

#!/bin/bash
#
######################################################################################
#
# Allows a list of additions to PATH with no dupes
#
# Patch code below into your $HOME/.bashrc file or where it
# will be seen at login.
#
# Can also be made executable and run as-is.
#
######################################################################################


# add2path=($HOME/bin .)                  ## uncomment space separated list
if [ $add2path ]; then                    ## skip if list empty or commented out
for nodup in ${add2path[*]}
do
case $PATH in                 ## case block thanks to MIKE511
$nodup:* | *:$nodup:* | *:$nodup ) ;;    ## if found, do nothing
*) PATH=$PATH:$nodup          ## else, add it to end of PATH or
esac                          ## *) PATH=$nodup:$PATH   prepend to front
done
export PATH
fi
## debug add2path
echo
echo " PATH == $PATH"
echo

如果启用了扩展的 globbing,就可以做到以下几点:

# delete all /opt/local paths in PATH
shopt -s extglob
printf "%s\n" "${PATH}" | tr ':' '\n' | nl
printf "%s\n" "${PATH//+(\/opt\/local\/)+([^:])?(:)/}" | tr ':' '\n' | nl


man bash | less -p extglob

扩展的全球化一行程序(好吧,算是吧) :

path_remove ()  { shopt -s extglob; PATH="${PATH//+(${1})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; }

似乎没有必要逃避1美元的削减。

path_remove ()  { shopt -s extglob; declare escArg="${1//\//\\/}"; PATH="${PATH//+(${escArg})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; }

既然替换的大问题是最终案例,那么如何使最终案例与其他案例没有区别呢?如果路径在开始和结束处已经有冒号,我们可以简单地搜索用冒号包装的所需字符串。事实上,我们可以很容易地添加这些冒号,然后删除它们。

# PATH => /bin:/opt/a dir/bin:/sbin
WORK=:$PATH:
# WORK => :/bin:/opt/a dir/bin:/sbin:
REMOVE='/opt/a dir/bin'
WORK=${WORK/:$REMOVE:/:}
# WORK => :/bin:/sbin:
WORK=${WORK%:}
WORK=${WORK#:}
PATH=$WORK
# PATH => /bin:/sbin

纯 Bash:)。

是的,例如,将冒号放在 PATH 的末尾,可以使移除路径变得不那么笨拙和容易出错。

path_remove ()  {
declare i newPATH
newPATH="${PATH}:"
for ((i=1; i<=${#@}; i++ )); do
#echo ${@:${i}:1}
newPATH="${newPATH//${@:${i}:1}:/}"
done
export PATH="${newPATH%:}"
return 0;
}


path_remove_all ()  {
declare i newPATH
shopt -s extglob
newPATH="${PATH}:"
for ((i=1; i<=${#@}; i++ )); do
newPATH="${newPATH//+(${@:${i}:1})*([^:]):/}"
#newPATH="${newPATH//+(${@:${i}:1})*([^:])+(:)/}"
done
shopt -u extglob
export PATH="${newPATH%:}"
return 0
}


path_remove /opt/local/bin /usr/local/bin


path_remove_all /opt/local /usr/local

在 PATH 中添加冒号,我们还可以这样做:

path_remove ()  {
declare i newPATH
# put a colon at the beginning & end AND double each colon in-between
newPATH=":${PATH//:/::}:"
for ((i=1; i<=${#@}; i++)); do
#echo ${@:${i}:1}
newPATH="${newPATH//:${@:${i}:1}:/}"   # s/:\/fullpath://g
done
newPATH="${newPATH//::/:}"
newPATH="${newPATH#:}"      # remove leading colon
newPATH="${newPATH%:}"      # remove trailing colon
unset PATH
PATH="${newPATH}"
export PATH
return 0
}




path_remove_all ()  {
declare i newPATH extglobVar
extglobVar=0
# enable extended globbing if necessary
[[ ! $(shopt -q extglob) ]]  && { shopt -s extglob; extglobVar=1; }
newPATH=":${PATH}:"
for ((i=1; i<=${#@}; i++ )); do
newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}"     # s/:\/path[^:]*//g
done
newPATH="${newPATH#:}"      # remove leading colon
newPATH="${newPATH%:}"      # remove trailing colon
# disable extended globbing if it was enabled in this function
[[ $extglobVar -eq 1 ]] && shopt -u extglob
unset PATH
PATH="${newPATH}"
export PATH
return 0
}


path_remove /opt/local/bin /usr/local/bin


path_remove_all /opt/local /usr/local

在 path _ remove _ all 中(通过代理) :

-newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}"
+newPATH="${newPATH//:${@:${i}:1}*([^:])/}"        # s/:\/path[^:]*//g

如果您担心删除 $PATH 中的 复制品,恕我直言,最优雅的方法是首先不添加它们。第一行:

if ! $( echo "$PATH" | tr ":" "\n" | grep -qx "$folder" ) ; then PATH=$PATH:$folder ; fi

$file 可以被任何东西替换,并且可以包含空格(“/home/user/my document”)

虽然这是一个非常古老的主题,但我认为这个解决方案可能会引起人们的兴趣:

PATH="/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
REMOVE="ccache" # whole or part of a path :)
export PATH=$(IFS=':';p=($PATH);unset IFS;p=(${p[@]%%$REMOVE});IFS=':';echo "${p[*]}";unset IFS)
echo $PATH # outputs /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

在这个 博客文章上找到的。我想我最喜欢这个:)

我采取了一种与这里的大多数人略有不同的方法,专注于字符串操作,如下所示:

path_remove () {
if [[ ":$PATH:" == *":$1:"* ]]; then
local dirs=":$PATH:"
dirs=${dirs/:$1:/:}
export PATH="$(__path_clean $dirs)"
fi
}
__path_clean () {
local dirs=${1%?}
echo ${dirs#?}
}

上面是我使用的最终函数的简化示例。我还创建了 path_add_beforepath_add_after,允许在 PATH 中已有的指定路径之前/之后插入路径。

完整的函数集在我的 Dotfiles中的 Path _ helpers. sh中可用。它们完全支持在 PATH 字符串的开头/中间/结尾处删除/附加/预置/插入。

我一直在使用 bash 发行版中的函数,这些函数显然从1991年就已经存在了。这些仍然在 Fedora 的 bash-docs 包中,并且过去在 /etc/profile中使用,但是现在不再使用..。

$ rpm -ql bash-doc |grep pathfunc
/usr/share/doc/bash-4.2.20/examples/functions/pathfuncs
$ cat $(!!)
cat $(rpm -ql bash-doc |grep pathfunc)
#From: "Simon J. Gerraty" <sjg@zen.void.oz.au>
#Message-Id: <199510091130.VAA01188@zen.void.oz.au>
#Subject: Re: a shell idea?
#Date: Mon, 09 Oct 1995 21:30:20 +1000




# NAME:
#       add_path.sh - add dir to path
#
# DESCRIPTION:
#       These functions originated in /etc/profile and ksh.kshrc, but
#       are more useful in a separate file.
#
# SEE ALSO:
#       /etc/profile
#
# AUTHOR:
#       Simon J. Gerraty <sjg@zen.void.oz.au>


#       @(#)Copyright (c) 1991 Simon J. Gerraty
#
#       This file is provided in the hope that it will
#       be of use.  There is absolutely NO WARRANTY.
#       Permission to copy, redistribute or otherwise
#       use this file is hereby granted provided that
#       the above copyright notice and this notice are
#       left intact.


# is $1 missing from $2 (or PATH) ?
no_path() {
eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
[ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
[ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

迄今为止我发现的最优雅的纯 bash 解决方案:

pathrm () {
local IFS=':'
local newpath
local dir
local pathvar=${2:-PATH}
for dir in ${!pathvar} ; do
if [ "$dir" != "$1" ] ; then
newpath=${newpath:+$newpath:}$dir
fi
done
export $pathvar="$newpath"
}


pathprepend () {
pathrm $1 $2
local pathvar=${2:-PATH}
export $pathvar="$1${!pathvar:+:${!pathvar}}"
}


pathappend () {
pathrm $1 $2
local pathvar=${2:-PATH}
export $pathvar="${!pathvar:+${!pathvar}:}$1"
}
function __path_remove(){
local D=":${PATH}:";
[ "${D/:$1:/:}" != "$D" ] && PATH="${D/:$1:/:}";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
}

从我的.bashrc 文件中找到的。 当您使用 PATH 时,如果它丢失了,awk/sed/grep 将变得不可用: -)

这里有一个一行程序,尽管当前的 接受最高评价的答案,不添加不可见的字符到 PATH 和可以处理路径,包含空格:

export PATH=$(p=$(echo $PATH | tr ":" "\n" | grep -v "/cygwin/" | tr "\n" ":"); echo ${p%:})

就我个人而言,我也发现这很容易阅读/理解,而且它只涉及常见的命令,而不是使用 awk。

到目前为止,我发现的最佳纯 bash 选项如下:

function path_remove {
# Delete path by parts so we can never accidentally remove sub paths
PATH=${PATH//":$1:"/":"} # delete any instances in the middle
PATH=${PATH/#"$1:"/} # delete any instance at the beginning
PATH=${PATH/%":$1"/} # delete any instance at the end
}

这是基于超级用户上的 不太正确的答案将目录添加到 $PATH (如果还没有),修复评论中提到的问题。

显然,如果你不想要解释性的注释,这可以做成一个单行函数。

拖尾’:’是由于您正在设置行结束符,而不是分隔符。我使用资源有限的单位,喜欢把所有东西打包成一个单一的脚本,没有这些奇怪的东西:

path_remove () {
PATH="$(echo -n $PATH | awk -v RS=: -v ORS= '$0 != "'$1'"{print s _ $0;s=":"}')"
}

大多数其他建议的解决方案只依赖于字符串匹配,而不考虑包含特殊名称(如 ...~)的路径段。下面的 bash 函数在其参数和路径段中解析目录字符串,以查找逻辑目录匹配和字符串匹配。

rm_from_path() {
pattern="${1}"
dir=''
[ -d "${pattern}" ] && dir="$(cd ${pattern} && pwd)"  # resolve to absolute path


new_path=''
IFS0=${IFS}
IFS=':'
for segment in ${PATH}; do
if [[ ${segment} == ${pattern} ]]; then             # string match
continue
elif [[ -n ${dir} && -d ${segment} ]]; then
segment="$(cd ${segment} && pwd)"                 # resolve to absolute path
if [[ ${segment} == ${dir} ]]; then               # logical directory match
continue
fi
fi
new_path="${new_path}${IFS}${segment}"
done
new_path="${new_path/#${IFS}/}"                       # remove leading colon, if any
IFS=${IFS0}


export PATH=${new_path}
}

测试:

$ mkdir -p ~/foo/bar/baz ~/foo/bar/bif ~/foo/boo/bang
$ PATH0=${PATH}
$ PATH=~/foo/bar/baz/.././../boo/././../bar:${PATH}  # add dir with special names
$ rm_from_path ~/foo/boo/../bar/.  # remove same dir with different special names
$ [ ${PATH} == ${PATH0} ] && echo 'PASS' || echo 'FAIL'

这当然是优雅的,但它确实使用了外部 sed。 此外,它删除了包含搜索字符串 $1的所有路径。它也不会留下一个悬空: 在已删除的路径是 PATH 上最后一个路径的情况下。

PATH=`echo $PATH | sed 's/:[^:]*$1[^:]*//g'`

然而,这种选择确实留下了悬而未决的结局。

PATH=`echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":"`

没有反勾的备选方案是:

PATH=$(echo $PATH | sed 's/:[^:]*$1[^:]*//g')


PATH=$(echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":")

这里有一个解决方案:

  • 是纯粹的巴斯,
  • 不调用其他进程(如‘ sed’或‘ awk’) ,
  • 不改变 IFS,
  • 不会分叉子弹壳,
  • 处理带有空格的路径,以及
  • 移除 PATH中参数的所有匹配项。

    removeFromPath() {
    local p d
    p=":$1:"
    d=":$PATH:"
    d=${d//$p/:}
    d=${d/#:/}
    PATH=${d/%:/}
    }

Linux From Scratch 在 /etc/profile中定义了三个 Bash 函数:

# Functions to help us manage paths.  Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
local IFS=':'
local NEWPATH
local DIR
local PATHVARIABLE=${2:-PATH}
for DIR in ${!PATHVARIABLE} ; do
if [ "$DIR" != "$1" ] ; then
NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
fi
done
export $PATHVARIABLE="$NEWPATH"
}


pathprepend () {
pathremove $1 $2
local PATHVARIABLE=${2:-PATH}
export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}


pathappend () {
pathremove $1 $2
local PATHVARIABLE=${2:-PATH}
export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}


export -f pathremove pathprepend pathappend

档号: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html

我喜欢@BenBlank 对其原始问题的更新中显示的三个函数。为了概括它们,我使用了一个2个参数的表单,它允许我设置 PATH 或任何其他我想要的环境变量:

path_append ()  { path_remove $1 $2; export $1="${!1}:$2"; }
path_prepend () { path_remove $1 $2; export $1="$2:${!1}"; }
path_remove ()  { export $1="`echo -n ${!1} | awk -v RS=: -v ORS=: '$1 != "'$2'"' | sed 's/:$//'`"; }

使用例子:

path_prepend PATH /usr/local/bin
path_append PERL5LIB "$DEVELOPMENT_HOME/p5/src/perlmods"

注意,我还添加了一些引号,以便对包含空格的路径名进行适当的处理。

在 Bash 中从 $PATH 变量中删除路径的最优雅的方法是什么?

还有什么比“ awk”更优雅的呢?

path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`;

巨蟒!它是一个更易于阅读和维护的解决方案,并且很容易检查它是否真的在做您想要的事情。

要删除第一个 path 元素吗?

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"

(与从 echo通过管道传输相比,os.getenv['PATH']会短一些,并且提供与上面相同的结果,但是我担心 Python 可能会用这个环境变量做一些事情,所以最好直接从您关心的环境中通过管道传输。)

类似地从末端移除:

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"

例如,为了使这些可重用的 shell 函数能够保存在.bashrc 文件中:

strip_path_first () {
PATH="$(echo "$PATH" |
python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"
}


strip_path_last () {
PATH="$(echo "$PATH" |
python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"
}

我知道这个问题是关于 BASH 的,每个人都应该更喜欢它,但是因为我喜欢对称性,有时我需要使用“ csh”,所以我构建了与上面的“ path _ prepend ()”、“ path _ append ()”和“ path _ remove ()”等价的优雅解决方案。

要点是“ csh”没有函数,所以我在我的个人 bin 目录中放置了一些与函数类似的 shell 脚本。我创建别名来源于这些脚本,从而进行指定的环境变量更改。

~/bin/_ path _ remove. csh:

set _resolve = `eval echo $2`
setenv $1 `eval echo -n \$$1 | awk -v RS=: -v ORS=: '$1 != "'${_resolve}'"' | sed 's/:$//'`;
unset _resolve

~/bin/_ path _ append. csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_base}:${_resolve}
unset _base _resolve

~/bin/_ path _ prepend. csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_resolve}:${_base}
unset _base _resolve

~/bin/. chrc:

…
alias path_remove  "source ~/bin/_path_remove.csh  '\!:1' '\!:2'"
alias path_append  "source ~/bin/_path_append.csh  '\!:1' '\!:2'"
alias path_prepend "source ~/bin/_path_prepend.csh '\!:1' '\!:2'"
…

你可以像这样使用它们..。

%(csh)> path_append MODULEPATH ${HOME}/modulefiles

让我们从 PATH 中“移除”对/PATH/to/something 的引用:

巴斯:

PATH=${PATH/something/nope/}

Windows 批处理的额外答案:

set PATH=%PATH:something=nope%

有人有更简单的解决方案吗? :)

Bash 内置的泛线笔(不去除重复) : PATH=${PATH/${PATH/#$DIR:*/$DIR:}/}${PATH/${PATH/*:$DIR*/:$DIR}/}