首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在shell脚本中操作$PATH元素?

如何在shell脚本中操作$PATH元素?
EN

Stack Overflow用户
提问于 2008-11-07 22:52:14
回答 12查看 22.8K关注 0票数 32

是否有一种从路径类shell变量中删除元素的惯用方法?

那就是我想要

代码语言:javascript
复制
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

EN

回答 12

Stack Overflow用户

回答已采纳

发布于 2008-11-16 15:53:15

处理dmckee提出的解决方案:

  1. 虽然有些版本的Bash可能允许连字符出现在函数名中,但其他版本(MacOS X)则不允许。
  2. --我不认为有必要在函数结束之前使用返回。
  3. --我不认为所有的分号都需要。
  4. --我不明白为什么您有逐模式导出值的路径元素。把export看作相当于设置(甚至创建)一个全局变量--只要有可能就可以避免。
  5. --我不知道你期望'replace-path PATH $PATH /usr‘做什么,但它做不到我想做的事情。

考虑一个开始包含以下内容的路径值:

代码语言:javascript
复制
.
/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')是:

代码语言:javascript
复制
.
/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命令来修复这个问题:

代码语言:javascript
复制
export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
               tr "\n" ":" | sed "s|::|:|g")

我使用了':‘而不是’\‘来分隔替代词的各个部分,因为’AC.26‘(理论上)可以出现在PATH组件中,而根据路径的定义,冒号不能出现。我观察到,第二个sed可以从路径的中间消除当前目录。也就是说,路径的合法(尽管是不正常的)值可以是:

代码语言:javascript
复制
PATH=/bin::/usr/local/bin

处理后,当前目录将不再位于路径上。

用于锚定匹配的类似更改在path-element-by-pattern中是合适的。

代码语言:javascript
复制
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")

我顺便指出,grep -m 1是不标准的(它是一个GNU扩展,也可以在MacOS X上使用)。而且,实际上,用于-necho选项也是不标准的;您最好简单地删除尾冒号,后者是通过将换行符从回显转换为冒号而添加的。由于只使用一次path元素的模式,就会产生不良的副作用(它会破坏任何先前存在的名为$removestr的导出变量),因此它可以被它的身体明智地替换。这与更自由地使用引号以避免空格或不必要的文件名扩展的问题一起导致:

代码语言:javascript
复制
# 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"
}

我有一个名为echopath的Perl脚本,它在调试类似路径变量的问题时很有用:

代码语言:javascript
复制
#!/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";
    }
}

当我在下面的测试代码上运行修改后的解决方案时:

代码语言:javascript
复制
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

产出如下:

代码语言:javascript
复制
.
/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。如果您的函数能够做到这一点,那么用户就可以输入:

代码语言:javascript
复制
replace_path PATH /usr/bin /work/bin

这可以通过以下方法实现:

代码语言:javascript
复制
list=$(eval echo '$'$path)

这导致了对守则的修订:

代码语言:javascript
复制
# 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"
}

下面的修订测试现在也起作用了:

代码语言:javascript
复制
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

它产生与以前相同的输出。

票数 19
EN

Stack Overflow用户

发布于 2008-12-17 02:18:03

将我的答案转发给What is the most elegant way to remove a path from the $PATH variable in Bash?

代码语言:javascript
复制
#!/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[*]}"

或者只有一条线:

代码语言:javascript
复制
PATH=$(IFS=':';t=($PATH);unset IFS;t=(${t[@]%%*usr*});IFS=':';echo "${t[*]}");
票数 7
EN

Stack Overflow用户

发布于 2008-11-07 23:31:30

要删除元素,可以使用sed:

代码语言:javascript
复制
#!/bin/bash
NEW_PATH=$(echo -n $PATH | tr ":" "\n" | sed "/foo/d" | tr "\n" ":")
export PATH=$NEW_PATH

将从路径中删除包含"foo“的路径。

还可以使用sed在给定行之前或之后插入新行。

编辑:您可以通过排序和uniq管道删除重复项:

代码语言:javascript
复制
echo -n $PATH | tr ":" "\n" | sort | uniq -c | sed -n "/ 1 / s/.*1 \(.*\)/\1/p" | sed "/foo/d" | tr "\n" ":"
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/273909

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档