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


114

或更笼统地说,如何从Bash环境变量中以冒号分隔的列表中删除项目?

我以为几年前我已经看过一种简单的方法,可以使用更高级的Bash变量扩展形式,但是如果这样的话,我就会迷失方向。快速搜索Google却发现几乎没有相关结果,而我也称其为“简单”或“优雅”。例如,分别使用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对awkRS变量的巧妙使用。

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来删除尾随的冒号。考虑到马丁解决方案的其余部分是多么简单,我很愿意接受它!


相关问题: 如何在Shell脚本中操作$ PATH元素?


对于任何变量:WORK=`echo -n ${1} | awk -v RS=: -v ORS=: '$0 != "'${3}'"' | sed 's/:$//'`; eval "export ${2}=${WORK}"但您必须将其称为func $VAR VAR pattern(基于@ martin-york和@ andrew-aylett)
vesperto

Answers:


51

一分钟的awk:

# 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/:*$//')

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


1
尝试删除最后一个或多个元素时失败:在第一种情况下,它添加当前目录(作为空字符串;可能的安全漏洞),在第二种情况下,它添加``作为路径元素。
弗雷德·富

1
@larsmans:对我来说很好。注意:Empty与当前目录“ ./”不同
Martin York

2
作为特殊规则,作为PATH变量“成员”的空字符串的确表示所有Unix Shell中的当前目录,至少从1979年的V7 Unix起bash。检查手册或自己尝试。
Fred Foo

1
@Martin:POSIX不需要此行为,但会记录并允许它:pubs.opengroup.org/onlinepubs/9699919799/basedefs/…–
Fred Foo

1
删除最后一个元素时,还有一个更细微的问题:awk似乎在字符串的末尾添加了一个空字节。这意味着,如果稍后将另一个目录附加到PATH,则实际上将不会搜索该目录。
sschuberth 2012年

55

我的脏话:

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

18
这是不是一个好迹象时,最明显的解决办法是 -automate过程。:-D
本·布兰克

比“优雅”的替代品安全得多。
Jortstek

44

既然替代的最大问题是最终案例,那么如何使最终案例与其他案例没有不同呢?如果路径的开头和结尾已经有冒号,我们可以简单地搜索所需的用冒号包裹的字符串。照原样,我们可以轻松添加这些冒号,然后再将其删除。

# 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

纯扑:)。


2
我将在本教程部分添加一些额外的知识:tldp.org/LDP/abs/html/string-manipulation.html
Cyber​​ Oliveira,

1
我使用它是因为它看起来是最简单的解决方案。它非常快捷,简便,您可以在实际更改PATH变量的最后一行之前,通过echo $ WORK轻松检查工作。
菲尔·格兰

2
绝对是一点宝石。当我找到此帖子时,正是我想要做的。-谢谢安德鲁!顺便说一句:也许您想在“:$ PATH:”周围添加双引号,以防万一它应该包含空格(与“ / usr / bin”相同)和最后一行“ $ WORK”。

2
谢谢@ PacMan-- :)。我很确定(尝试一下),您不需要分配空间WORKPATH因为在将行解析为变量分配和命令执行的部分后,变量扩展才会发生。 REMOVE可能需要用引号引起来,或者如果它是常量,则可以直接将其放入替换项中。
Andrew Aylett 2013年

保存字符串时,我总是感到困惑,因此,我总是开始将字符串双引号。再次感谢您澄清这一点。:)

26

这是我可以设计的最简单的解决方案:

#!/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”以仅删除该元素。

更新sschuberth

即使我认为a $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[*]}"

2
作为一个衬里:PATH = $(IFS =':'; t =($ PATH); unset IFS; t =($ {t [@] %% * usr *}); IFS =':'; echo“ $ {t [*]}“));
nicerobot

1
该解决方案不适用于PATH中包含空格的路径;它用冒号代替它们。
sschuberth 2012年

11

尽管目前已经接受,但这是一线并给出了最高评分的答案,但它不会在PATH中添加不可见字符,并且可以处理包含空格的路径:

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

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


2
...,如果您希望使用文件名中的换行符甚至可以解决的问题,则可以使用以下命令:( export PATH=$(p=$(echo $PATH | tr ":" "\0" | grep -v -z "/cygwin/" | tr "\0" ":"); echo ${p%:}) 尽管可以说,如果这样做,您可能要问自己为什么需要这样做:))
ehdr,2015年

这将删除部分匹配项,这可能不是您想要的;我会用grep -v "^/path/to/remove\$"grep -v -x "/path/to/remove"
ShadSterling

好的解决方案,但是您真的认为tr比这更普遍awk吗?;)
K.-Michael Aye

1
绝对。轻量级环境(如Windows上的Git Bash)带有一个简单的工具,tr而不是诸如的解释器awk
sschuberth '16

1
@ehdr:您需要更换echo "..."printf "%s" "..."为它像路径工作-e和类似。请参阅stackoverflow.com/a/49418406/102441
Eric

8

这是一个解决方案:

  • 是纯Bash,
  • 不调用其他进程(例如“ sed”或“ awk”),
  • 不变 IFS
  • 不分叉子壳,
  • 处理带有空格的路径,并且
  • 删除所有出现的参数 PATH

    removeFromPath(){
       本地PD
       p =“:$ 1:”
       d =“:$ PATH:”
       d = $ {d // $ p /:}
       d = $ {d /#:/}
       PATH = $ {d /%:/}
    }

4
我喜欢这个解决方案。也许使变量名更具描述性?
Anukool

非常好。但是,不适用于包含星号(*)的路径段。有时他们偶然到达那里。
约尔格

6

函数__path_remove(){
本地D =“:$ {PATH}:”;
[“ $ {D /:$ 1:/:}”!=“ $ D”] && PATH =“ $ {D /:$ 1:/:}”;
PATH =“ $ {PATH /#:/}”;
export PATH =“ $ {PATH /%:/}”;
}

从我的.bashrc文件中挖出来。当您在使用PATH时,它迷路了,awk / sed / grep变得不可用:-)


1
这是非常好的一点。(我从来不喜欢为诸如此类的简单事情执行外部实用程序)。

6

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

function path_remove {
  PATH=${PATH/":$1"/} # delete any instances in the middle or at the end
  PATH=${PATH/"$1:"/} # delete any instances at the beginning
}

这是基于将目录添加到$ PATH的不太正确的答案如果超级用户还没有将其添加到$ PATH


这也很好。我测试了 如果PATH中有重复的路径(例如,两个完全相同),则仅删除其中一个。您也可以将它变成removePath () { PATH=${PATH/":$1"/}; PATH=${PATH/"$1:"/}; }

$PATH包含目标(即要删除)路径的子文件夹时,此解决方案将失败。例如:a:abc/def/bin:b-> a/bin:b,何时abc/def删除。
罗宾·许

5

我一直在使用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;:\$;;"`
}

4

我确实在这里写了一个答案(也使用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:

无论如何,对于相同的问题以及很多有用的答案,请参见此处


如果要删除包含部分字符串的行,请使用awk '$0 !~ "/bin"'。即使用awk运算符保留不包含'/ bin'的行!~
thoni56 '19

3

好吧,在bash中,由于它支持正则表达式,因此我将简单地执行以下操作:

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

不仅是路径名扩展,不是正则表达式吗?
dreamlax

2
虽然bash确实支持正则表达式(从bash 3开始),但这不是示例,而是变量替换。
罗伯特·格兰伯

4
这是模式变量扩展,解决方案有几个问题。1)它不匹配第一个元素。2)它将匹配以“ / home / user / bin”开头的所有内容,而不仅仅是“ / home / user / bin”。3)它需要转义特殊字符。充其量,我想说的是一个不完整的例子。
nicerobot

2

我喜欢@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"

请注意,我还添加了一些引号以允许对包含空格的路径名进行正确处理。


2

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

有什么比awk更优雅?

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

蟒蛇!这是一个更具可读性和可维护性的解决方案,很容易检查它是否确实在按照您的要求进行操作。

说您要删除第一个路径元素?

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

(而不是from的管道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))")"
}

1

是的,例如,在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 

1

如果您担心要删除$ PATH中的重复项,恕我直言,最优雅的方法是不要首先添加它们。在1行中:

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

$ folder可以用任何东西替换,并且可以包含空格(“ / home / user / mydocuments”)


1

我迄今为止发现的最优雅的纯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"                                  
} 

1

其他大多数建议的解决方案只对字符串匹配依赖,不考虑含特殊的名字,如路径段...~。下面的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'

外框再加上一个
马丁·约克

1

Scratch的Linux在以下方面定义了三个Bash函数/etc/profile

# 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


1

我知道这个问题问的是每个人都应该喜欢的BASH,但是由于我喜欢对称性,有时需要使用“ csh”,所以我构建了等效于“ path_prepend()”,“ path_append()”和“ path_remove” ()“上方的优雅解决方案。

要点是“ csh”没有功能,因此我在个人bin目录中放入了一些类似于功能的shell脚本。我为这些脚本创建SOURCE的别名,以更改指定的环境变量。

〜/ 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 / .cshrc:


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

0

由于这往往会带来很大的问题,因为没有优雅的方法,所以我建议通过重新布置解决方案来避免该问题:建立您的PATH,而不是尝试将其拆除。

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

软件构建的一个常见问题是它在某些机器上中断,最终是由于有人配置了其默认外壳程序(PATH和其他环境变量)而导致的。完美的解决方案是通过完全指定外壳环境来使您的构建脚本不受干扰。对您的构建脚本进行编码,以根据您控制的组装件(例如编译器,库,工具,组件等的位置)设置PATH和其他环境变量。使每个可配置项都可以单独设置,验证和设置然后在脚本中适当使用

例如,我有一个在新雇主那里继承的基于Maven的,以WebLogic为目标的Java构建。该构建脚本因脆弱而臭名昭著,另一位新员工和我花了三周的时间(不是全职,只是在这里和那里,但仍然是几个小时)来使其在我们的机器上工作。至关重要的一步是我控制了PATH,以便我确切地知道正在调用哪个Java,哪个Maven和哪个WebLogic。我创建了环境变量以指向每个工具,然后基于这些工具以及其他一些工具计算了PATH。类似的技术已经驯服了其他可配置的设置,直到我们最终创建了可复制的构建。

顺便说一句,不要使用Maven,Java没关系,只有在绝对需要WebLogic集群时才购买WebLogic(但是没有,尤其是没有其专有功能)。

最好的祝愿。


有时您没有root权限,管理员则管理您的PATH。当然,您可以自己构建,但是每次管理员移动某些内容时,都必须弄清楚他的放置位置。这样的安排破坏了拥有管理员的目的。
2012年

0

与@litb一样,我为“ 如何在shell脚本中操作$ PATH元素 ”问题提供了答案,因此我的主要答案在那里。

使用场间分隔符bash可以最轻松地实现Bourne外壳导数和其他Bourne外壳导数中的“拆分”功能$IFS。例如,要设置的位置参数($1$2,...)来的路径,使用元素:

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

只要$ PATH中没有空格,它就可以正常工作。使之适用于包含空格的路径元素是一项不平凡的工作-留给感兴趣的读者。使用诸如Perl之类的脚本语言来处理它可能更简单。

我也有一个脚本,clnpath我广泛使用它来设置PATH。我在“ 如何避免在csh中复制PATH变量 ”的答案中记录了该文档。


IFS =:a =($ PATH); IFS =拆分也很好。如果它们也包含空格,则可以工作。但是随后您得到了一个数组,并且不得不弄乱for循环等来删除名称。
Johannes Schaub-litb

是; 它变得很奇怪-正如我的最新评论一样,此时使用脚本语言可能更简单。
乔纳森·莱夫勒

0

使这个问题烦人的是第一要素和最后要素之间的栅栏案例。通过更改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"

0

这是Perl的单行代码:

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

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


0

好东西 我用这个来避免添加重复项。

#!/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

1
您可以通过在PATH字符串中添加前后冒号来简化case语句:case ":$PATH:" in (*:"$nodup":*) ;; (*) PATH="$PATH:$nodup" ;; esac
glenn jackman 2011年

0

启用扩展的globing后,可以执行以下操作:

# 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

0

扩展的单线球(很好,有点):

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; } 

0

将冒号添加到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 

0

在path_remove_all中(通过proxxy):

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

0

尽管这是一个非常老的线程,但我认为此解决方案可能很有趣:

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

在此博客文章中找到了它。我想我最喜欢这个:)


0

我采用的方法与这里的大多数人略有不同,并且只专注于字符串操作,例如:

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字符串的开始/中间/结尾处删除/附加/前置/插入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.