当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在shell脚本中操作$PATH元素?》经验,为你挑选了2个好方法。

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

那是我想要的

PATH=/home/joe/bin:/usr/local/bin:/usr/bin:/bin:/path/to/app/bin:.

删除替换/path/to/app/bin,而不重挫可变的其余部分.允许我新元素放在任意位置的额外点数.目标将由明确定义的字符串识别,并且可以在列表中的任何点处发生.

我知道我已经看到了这一点,并且可以自己拼凑一些东西,但我正在寻找一个很好的方法.便携性和标准化加分.

我使用bash,但欢迎你最喜欢的shell中的例子.


这里的上下文是需要在多个版本之间方便地切换(一个用于进行分析,另一个用于处理框架)的大型科学分析包,它产生几十个可执行文件,数据存储在文件系统周围,并使用环境变量帮助找到所有这些东西.我想编写一个选择版本的脚本,并且需要能够删除$PATH与当前活动版本相关的元素,并用与新版本相关的相同元素替换它们.


这与$PATH在重新运行登录脚本等时防止重复元素的问题有关.


以前的类似问题:如何避免在csh中复制路径变量

随后的类似问题:从Bash中的$ PATH变量中删除路径的最优雅方法是什么?

Jonathan Lef.. 19

从dmckee处理建议的解决方案:

    某些版本的Bash可能允许在函数名称中使用连字符,而其他版本(MacOS X)则不允许.

    我觉得在函数结束之前不需要立即使用return.

    我不认为需要所有的冒号.

    我不明白为什么你有逐个元素的路径输出值.可以认为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选择权echo也是非标准的; 你最好只删除由于将换行符从echo转换为冒号而添加的尾部冒号.由于path-element-by-pattern只使用一次,因此会产生不良的副作用(它会破坏任何预先存在的导出变量$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 
#
# To replace a path:
#    replace_path         PATH $PATH /exact/path/to/remove /replacement/path
#    replace_path_pattern PATH $PATH  /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 
#
# To replace a path:
#    replace_path         PATH /exact/path/to/remove /replacement/path
#    replace_path_pattern PATH  /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

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



1> Jonathan Lef..:

从dmckee处理建议的解决方案:

    某些版本的Bash可能允许在函数名称中使用连字符,而其他版本(MacOS X)则不允许.

    我觉得在函数结束之前不需要立即使用return.

    我不认为需要所有的冒号.

    我不明白为什么你有逐个元素的路径输出值.可以认为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选择权echo也是非标准的; 你最好只删除由于将换行符从echo转换为冒号而添加的尾部冒号.由于path-element-by-pattern只使用一次,因此会产生不良的副作用(它会破坏任何预先存在的导出变量$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 
#
# To replace a path:
#    replace_path         PATH $PATH /exact/path/to/remove /replacement/path
#    replace_path_pattern PATH $PATH  /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 
#
# To replace a path:
#    replace_path         PATH /exact/path/to/remove /replacement/path
#    replace_path_pattern PATH  /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

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



2> nicerobot..:

重新发布我的答案什么是从Bash中的$ 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=$(IFS=':';t=($PATH);unset IFS;t=(${t[@]%%*usr*});IFS=':';echo "${t[*]}");

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有