是否有一种从PATH类shell变量中删除元素的惯用方法?
那就是我想要
PATH=/home/joe/bin:/usr/local/bin:/usr/bin:/bin:/path/to/app/bin:.
和删除或替换 /path/to/app/bin
,而不会破坏变量的其余部分。允许我将新元素放在任意位置的额外点数。目标将通过定义良好的字符串识别,并且可以在列表中的任何位置发生。
我知道我已经看到了这一点,并且可以自己拼凑一些东西,但我正在寻找一个很好的方法。便携性和标准化加分。
我使用bash,但欢迎你最喜欢的shell中的例子。
这里的上下文是需要在多个版本之间方便地切换(一个用于进行分析,另一个用于处理框架)的大型科学分析包,它产生了几十个可执行文件,数据存储在文件系统周围,以及使用环境变量来帮助找到所有这些东西。我想编写一个选择版本的脚本,并且需要能够删除与当前活动版本相关的$PATH
元素,并用与新版本相关的相同元素替换它们。
这与在重新运行登录脚本等时阻止重复$PATH
元素的问题有关。
答案 0 :(得分:19)
从dmckee处理建议的解决方案:
export
等同于设置(甚至创建)全局变量 - 尽可能避免这种情况。replace-path PATH $PATH /usr
'的期望是什么,但它并没有达到我的预期。考虑一个开头的PATH值,包含:
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
我得到的结果(来自“replace-path PATH $PATH /usr
”)是:
.
/Users/jleffler/bin
/local/postgresql/bin
/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/local/bin
/bin
/bin
/sw/bin
/sbin
/sbin
我本来希望得到我的原始路径,因为/ usr不会作为(完整的)路径元素出现,只是作为路径元素的一部分。
可以通过修改replace-path
命令之一在sed
中修复此问题:
export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
tr "\n" ":" | sed "s|::|:|g")
我用':'而不是'|'从'|'开始分离替代品的部分可能(理论上)出现在路径组件中,而根据PATH的定义,冒号不能。我观察到第二个sed
可以从PATH中间消除当前目录。也就是说,PATH的合法(虽然有悖常理)值可能是:
PATH=/bin::/usr/local/bin
处理完毕后,当前目录将不再出现在PATH上。
在path-element-by-pattern
:
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")
我顺便指出grep -m 1
不是标准的(它是GNU扩展,也可以在MacOS X上使用)。事实上,-n
的{{1}}选项也是非标准的;你最好只删除由于将换行符从echo转换为冒号而添加的尾部冒号。由于path-element-by-pattern只使用一次,因此会产生不良副作用(它会破坏任何预先存在的名为echo
的导出变量),它可以被其身体明智地替换掉。这一点,以及更自由地使用引号来避免空格或不需要的文件名扩展问题,导致:
$removestr
我有一个名为# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace_path PATH $PATH /exact/path/to/remove
# replace_path_pattern PATH $PATH <grep pattern for target path>
#
# To replace a path:
# replace_path PATH $PATH /exact/path/to/remove /replacement/path
# replace_path_pattern PATH $PATH <target pattern> /replacement/path
#
###############################################################################
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a ":" delimited list to work from (e.g. $PATH)
# $3 the precise string to be removed/replaced
# $4 the replacement string (use "" for removal)
function replace_path () {
path=$1
list=$2
remove=$3
replace=$4 # Allowed to be empty or unset
export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" |
tr "\n" ":" | sed 's|:$||')
}
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a ":" delimited list to work from (e.g. $PATH)
# $3 a grep pattern identifying the element to be removed/replaced
# $4 the replacement string (use "" for removal)
function replace_path_pattern () {
path=$1
list=$2
removepat=$3
replacestr=$4 # Allowed to be empty or unset
removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$")
replace_path "$path" "$list" "$removestr" "$replacestr"
}
的Perl脚本,在调试类似PATH的变量时我觉得很有用:
echopath
当我在下面的测试代码上运行修改后的解决方案时:
#!/usr/bin/perl -w
#
# "@(#)$Id: echopath.pl,v 1.7 1998/09/15 03:16:36 jleffler Exp $"
#
# Print the components of a PATH variable one per line.
# If there are no colons in the arguments, assume that they are
# the names of environment variables.
@ARGV = $ENV{PATH} unless @ARGV;
foreach $arg (@ARGV)
{
$var = $arg;
$var = $ENV{$arg} if $arg =~ /^[A-Za-z_][A-Za-z_0-9]*$/;
$var = $arg unless $var;
@lst = split /:/, $var;
foreach $val (@lst)
{
print "$val\n";
}
}
输出结果为:
echo
xpath=$PATH
replace_path xpath $xpath /usr
echopath $xpath
echo
xpath=$PATH
replace_path_pattern xpath $xpath /usr/bin /work/bin
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath $xpath "/usr/.*/bin" /work/bin
echopath xpath
这对我来说是正确的 - 至少,我对问题的定义是正确的。
我注意到.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/work/bin
/bin
/sw/bin
/usr/sbin
/sbin
.
/Users/jleffler/bin
/work/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
评估echopath LD_LIBRARY_PATH
。如果你的功能能够做到这一点会很好,所以用户可以输入:
$LD_LIBRARY_PATH
可以通过使用:
来完成replace_path PATH /usr/bin /work/bin
这导致了对代码的修订:
list=$(eval echo '$'$path)
以下修订测试现在也适用:
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace_path PATH /exact/path/to/remove
# replace_path_pattern PATH <grep pattern for target path>
#
# To replace a path:
# replace_path PATH /exact/path/to/remove /replacement/path
# replace_path_pattern PATH <target pattern> /replacement/path
#
###############################################################################
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 the precise string to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace_path () {
path=$1
list=$(eval echo '$'$path)
remove=$2
replace=$3 # Allowed to be empty or unset
export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" |
tr "\n" ":" | sed 's|:$||')
}
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a grep pattern identifying the element to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace_path_pattern () {
path=$1
list=$(eval echo '$'$path)
removepat=$2
replacestr=$3 # Allowed to be empty or unset
removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$")
replace_path "$path" "$removestr" "$replacestr"
}
它产生与以前相同的输出。
答案 1 :(得分:5)
将我的回答重新发送到What is the most elegant way to remove a path from the $PATH variable in Bash?:
#!/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=$(IFS=':';t=($PATH);unset IFS;t=(${t[@]%%*usr*});IFS=':';echo "${t[*]}");
答案 2 :(得分:3)
要删除元素,可以使用sed:
#!/bin/bash
NEW_PATH=$(echo -n $PATH | tr ":" "\n" | sed "/foo/d" | tr "\n" ":")
export PATH=$NEW_PATH
将从路径中删除包含“foo”的路径。
您也可以使用sed在给定行之前或之后插入新行。
编辑:您可以通过排序和uniq:
来删除重复项echo -n $PATH | tr ":" "\n" | sort | uniq -c | sed -n "/ 1 / s/.*1 \(.*\)/\1/p" | sed "/foo/d" | tr "\n" ":"
答案 3 :(得分:2)
“How to keep from duplicating path variable in csh”的答案中有几个相关的程序。他们更专注于确保没有重复的元素,但我提供的脚本可以用作:
export PATH=$(clnpath $head_dirs:$PATH:$tail_dirs $remove_dirs)
假设$ head_dirs中有一个或多个目录,$ tail_dirs中有一个或多个目录,$ remove_dirs中有一个或多个目录,那么它使用shell将head,current和tail部分连接成一个巨大的值,并且然后从结果中删除$ remove_dirs中列出的每个目录(如果它们不存在则不是错误),以及消除路径中任何目录的第二次和后续出现。
这不涉及将路径组件放入特定位置(除了开头或结尾,而只是间接)。在符号上,指定要添加新元素的位置或要替换的元素是混乱的。
答案 4 :(得分:2)
请注意,bash本身可以进行搜索和替换。它可以完成你期望的所有正常“一次或全部”,[在]敏感选项中的情况。
从手册页:
$ {parameter / pattern / string}
扩展模式以生成模式,就像在路径名扩展中一样。扩展参数,并将模式与其值的最长匹配替换为字符串。如果Ipattern以/开头,则pattern的所有匹配都将替换为string。通常只替换第一场比赛。如果pattern以#开头,则它必须在参数的扩展值的开头匹配。如果pattern以%开头,则它必须在参数的扩展值的末尾匹配。如果string为null,则删除pattern的匹配,并且可以省略/ following模式。如果参数是@或 *,替换操作依次应用于每个位置参数,并且扩展是结果列表。如果参数是使用@或下标的数组变量 *,替换操作依次应用于数组的每个成员,扩展是结果列表。
您还可以通过将$ IFS(输入字段分隔符)设置为所需的分隔符来进行字段拆分。
答案 5 :(得分:1)
好的,感谢所有响应者。我准备了一个florin答案的封装版本。第一遍看起来像这样:
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace-path PATH $PATH /exact/path/to/remove
# replace-path-pattern PATH $PATH <grep pattern for target path>
#
# To replace a path:
# replace-path PATH $PATH /exact/path/to/remove /replacement/path
# replace-path-pattern PATH $PATH <target pattern> /replacement/path
#
###############################################################################
# Finds the _first_ list element matching $2
#
# $1 name of a shell variable to be set
# $2 name of a variable with a path-like structure
# $3 a grep pattern to match the desired element of $1
function path-element-by-pattern (){
target=$1;
list=$2;
pat=$3;
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 $pat);
return
}
# Removes or replaces an element of $1
#
# $1 name of the shell variable to set (i.e. PATH)
# $2 a ":" delimited list to work from (i.e. $PATH)
# $2 the precise string to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace-path () {
path=$1;
list=$2;
removestr=$3;
replacestr=$4; # Allowed to be ""
export $path=$(echo -n $list | tr ":" "\n" | sed "s|$removestr|$replacestr|" | tr "\n" ":" | sed "s|::|:|g");
unset removestr
return
}
# Removes or replaces an element of $1
#
# $1 name of the shell variable to set (i.e. PATH)
# $2 a ":" delimited list to work from (i.e. $PATH)
# $2 a grep pattern identifying the element to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace-path-pattern () {
path=$1;
list=$2;
removepat=$3;
replacestr=$4; # Allowed to be ""
path-element-by-pattern removestr $list $removepat;
replace-path $path $list $removestr $replacestr;
}
仍然需要在所有函数中捕获错误,我应该坚持使用重复的路径解决方案。
您可以通过在工作脚本中执行. /include/path/path_tools.bash
并调用replace-path*
函数来使用它。
我仍然愿意接受新的和/或更好的答案。
答案 6 :(得分:1)
使用awk很容易。
{
for(i=1;i<=NF;i++)
if($i == REM)
if(REP)
print REP;
else
continue;
else
print $i;
}
使用
启动它function path_repl {
echo $PATH | awk -F: -f rem.awk REM="$1" REP="$2" | paste -sd:
}
$ echo $PATH
/bin:/usr/bin:/home/js/usr/bin
$ path_repl /bin /baz
/baz:/usr/bin:/home/js/usr/bin
$ path_repl /bin
/usr/bin:/home/js/usr/bin
在指定位置插入。默认情况下,它会附加到最后。
{
if(IDX < 1) IDX = NF + IDX + 1
for(i = 1; i <= NF; i++) {
if(IDX == i)
print REP
print $i
}
if(IDX == NF + 1)
print REP
}
使用
启动它function path_app {
echo $PATH | awk -F: -f app.awk REP="$1" IDX="$2" | paste -sd:
}
$ echo $PATH
/bin:/usr/bin:/home/js/usr/bin
$ path_app /baz 0
/bin:/usr/bin:/home/js/usr/bin:/baz
$ path_app /baz -1
/bin:/usr/bin:/baz:/home/js/usr/bin
$ path_app /baz 1
/baz:/bin:/usr/bin:/home/js/usr/bin
这个保留了第一次出现。
{
for(i = 1; i <= NF; i++) {
if(!used[$i]) {
print $i
used[$i] = 1
}
}
}
像这样开始:
echo $PATH | awk -F: -f rem_dup.awk | paste -sd:
以下内容将为文件系统中不存在的所有条目打印错误消息,并返回非零值。
echo -n $PATH | xargs -d: stat -c %n
要简单检查所有元素是否都是路径并获取返回代码,您还可以使用test
:
echo -n $PATH | xargs -d: -n1 test -d
答案 7 :(得分:1)
假设
echo $PATH
/usr/lib/jvm/java-1.6.0/bin:lib/jvm/java-1.6.0/bin/:/lib/jvm/java-1.6.0/bin/:/usr/lib/qt-3.3/bin:/usr/lib/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/tvnadeesh/bin
如果你想删除/lib/jvm/java-1.6.0/bin/,请执行以下操作
export PATH=$(echo $PATH | sed 's/\/lib\/jvm\/java-1.6.0\/bin\/://g')
sed
将从echo $PATH
获取输入并将/lib/jvm/java-1.6.0/bin/替换为空
答案 8 :(得分:1)
说你有 /富:/一些/路径:/一些/路径/ DIR1:/一些/路径/ DIR2:/巴 而你想要替换 /一些/路径 然后它正确地替换“/ some / path”但是 留下“/ some / path / dir1”或“/ some / path / dir2”,就像你期望的那样。
function __path_add(){
if [ -d "$1" ] ; then
local D=":${PATH}:";
[ "${D/:$1:/:}" == "$D" ] && PATH="$PATH:$1";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
fi
}
function __path_remove(){
local D=":${PATH}:";
[ "${D/:$1:/:}" != "$D" ] && PATH="${D/:$1:/:}";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
}
# Just for the shake of completeness
function __path_replace(){
if [ -d "$2" ] ; then
local D=":${PATH}:";
if [ "${D/:$1:/:}" != "$D" ] ; then
PATH="${D/:$1:/:$2:}";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
fi
fi
}
相关文章 What is the most elegant way to remove a path from the $PATH variable in Bash?
答案 9 :(得分:1)
这些天,我喜欢使用ruby而不是awk / sed / foo之类的东西,所以这是我处理骗子的方法,
# add it to the path
PATH=~/bin/:$PATH:~/bin
export PATH=$(ruby -e 'puts ENV["PATH"].split(/:/).uniq.join(":")')
创建一个可重用的功能
mungepath() {
export PATH=$(ruby -e 'puts ENV["PATH"].split(/:/).uniq.join(":")')
}
一个红宝石衬里中的哈希,数组和字符串:)
答案 10 :(得分:0)
首先要改变一下字符串的一部分,就是sed替换。
例如: 如果echo $ PATH =&gt; “在/ usr /包装/斌:在/ usr / bin中:/ bin中:在/ usr /包装/游戏:在/ usr /包装/ X11R6 / bin中” 然后将“/ usr / bin”更改为“/ usr / local / bin”可以这样做:
##生成标准输出文件
##使用“=”字符而不是斜杠(“/”)因为那会很混乱, 在PATH中,#alternative引用字符不太可能
##路径分离符“:”在此处被删除并重新添加, #可能需要在最后一个路径后添加一个冒号
echo $ PATH | sed'= / usr / bin:= / usr / local / bin:='
此解决方案替换整个路径元素,因此如果new-element类似,则可能是多余的。
如果新的PATH'-s不是动态的,但总是在某个常量集中,你可以将那些保存在变量中并根据需要进行分配:
PATH = $ TEMP_PATH_1; #command ...; \ n PATH = $ TEMP_PATH_2; #command etc ...;
可能不是你在想什么。 bash / unix上的一些相关命令是:
PUSHD POPD 光盘 ls#可能l-1A为单列; 找 grep的 #可以确认该文件是您认为它来自哪里; ENV 型
..而且所有这些以及更多某些都会对PATH或目录产生影响。文本修改部分可以通过多种方式完成!
无论选择哪种解决方案都有4个部分:
1)按原样获取路径 2)解码路径以找到需要更改的部分 3)确定需要进行哪些更改/整合这些更改 4)验证/最终集成/设置变量
答案 11 :(得分:0)
与dj_segfault's answer一致,我在附加/预置可能多次执行的环境变量的脚本中执行此操作:
ld_library_path=${ORACLE_HOME}/lib
LD_LIBRARY_PATH=${LD_LIBRARY_PATH//${ld_library_path}?(:)/}
export LD_LIBRARY_PATH=${ld_library_path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
使用相同的技术删除,替换或操作PATH中的条目是微不足道的,因为类似于文件名扩展的模式匹配和shell参数扩展的模式列表支持。