这就是我想要的
PATH=/home/joe/bin:/usr/local/bin:/usr/bin:/bin:/path/to/app/bin:.
并删除或替换/ path / to / app / bin,而不会破坏其余的变量。额外的点,让我把新元素在任意位置。目标将可以由明确定义的字符串识别,并且可以发生在列表中的任何点。
我知道我已经看到这一切,并可能在一起我自己的东西,但我正在寻找一个不错的方法。可移植性和标准化一加。
我使用bash,但例子是欢迎在你最喜欢的shell以及。
这里的上下文需要在一个大的科学分析包的多个版本(一个用于分析,另一个用于框架)之间切换,产生几十个可执行文件,数据存储在文件系统,并使用环境变量帮助找到所有这些东西。我想编写一个选择版本的脚本,并且需要能够删除与当前活动版本相关的$ PATH元素,并用与新版本相关的相同元素替换它们。
这与在重新运行登录脚本等时防止重复的$ PATH元素的问题有关。
>上一个类似问题:How to keep from duplicating path variable in csh
>后续类似问题:What is the most elegant way to remove a path from the $PATH variable in Bash?
>虽然一些版本的Bash可能允许在函数名称中使用连字符,但其他(MacOS X)不能。
>我没有看到需要在函数结束之前立即使用返回。
>我没有看到所有的分号的需要。
>我不明白为什么你有path-element-by-pattern导出一个值。想想导出等同于设置(甚至创建)一个全局变量 – 尽可能避免的东西。
>我不知道你期望什么’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不显示为(完整)路径元素,只作为路径元素的一部分。
这可以通过修改sed命令之一在replace-path中修复:
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上。
锚定匹配的类似改变在路径逐个模式是适当的:
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")
我注意到传递grep -m 1不是标准(它是一个GNU扩展,也可在MacOS X)。而且,确实,回声的-n选项也是非标准的;你最好只是删除通过将换行符从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脚本echopath,我发现在调试问题类似PATH的变量时很有用:
#!/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" }
以下修改的测试现在也工作:
echo xpath=$PATH replace_path xpath /usr echopath xpath echo xpath=$PATH replace_path_pattern xpath /usr/bin /work/bin echopath xpath echo xpath=$PATH replace_path_pattern xpath "/usr/.*/bin" /work/bin echopath xpath
它产生与以前相同的输出。