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

2024-05-18

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

这就是我想要的

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

and remove or replace the /path/to/app/bin而不破坏变量的其余部分。允许我加分put任意位置的新元素。目标可以通过明确定义的字符串来识别,并且可以出现在列表中的任何点。

我知道我已经看到了这一点,并且可能可以自己拼凑一些东西,但我正在寻找一种好的方法。可移植性和标准化是一个优点。

我使用 bash,但也欢迎使用您最喜欢的 shell 中的示例。


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


这涉及到防止重复的问题$PATH重新运行登录脚本等时的元素。


  • 以前的类似问题:如何避免在 csh 中重复路径变量 https://stackoverflow.com/questions/135754/how-to-keep-from-duplicating-path-variable-in-csh
  • 随后类似的问题:在 Bash 中从 $PATH 变量中删除路径的最优雅的方法是什么? https://stackoverflow.com/questions/370047/what-is-the-most-elegant-way-to-remove-a-path-from-the-path-variable-in-bash

解决 dmckee 提出的解决方案:

  1. 虽然某些版本的 Bash 可能允许在函数名称中使用连字符,但其他版本 (MacOS X) 则不允许。
  2. 我认为不需要在函数结束之前立即使用 return 。
  3. 我不认为需要所有的分号。
  4. 我不明白为什么你要逐个路径元素导出一个值。考虑到export相当于设置(甚至创建)全局变量 - 尽可能避免这种情况。
  5. 我不确定你期望什么'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') is:

.
/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 转换为冒号而添加的尾随冒号。由于逐个路径元素仅使用一次,因此会产生不良副作用(它会破坏任何称为$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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 shell 脚本中操作 $PATH 元素? 的相关文章

  • 直接向pbs脚本传递参数

    有没有办法在提交作业之前直接将参数传递给 pbs 脚本 我需要循环遍历由不同数字表示的文件列表 并应用脚本来分析每个文件 我能想到的最好的办法如下 bin sh for i 1 i lt 10 i do export FILENUM i q
  • 这种 bash 文件名提取技术有何用途?

    我有一部分 bash 脚本正在获取不带扩展名的文件名 但我试图了解这里到底发生了什么 是做什么用的 有人可以详细说明 bash 在幕后做了什么吗 如何在一般基础上使用该技术 bin bash for src in tif do txt sr
  • shell中如何求数组的长度?

    shell中如何求数组的长度 例如 arr 1 2 3 4 5 我想得到它的长度 在本例中是 5 a 1 2 3 4 echo a 4
  • 检查 Bash 数组中是否存在元素[重复]

    这个问题在这里已经有答案了 我想知道是否有一种有效的方法来检查 Bash 数组中是否存在元素 我正在寻找类似于我可以在Python中做的事情 例如 arr a b c d if d in arr do your thing else do
  • 在Java中打印时差最惯用的方法是什么?

    我熟悉以毫秒为单位的打印时间差 long time System currentTimeMillis do something that takes some time long completedIn System currentTime
  • sh read 命令会吃掉输入中的反斜杠吗?

    也许最容易用一个例子来解释 echo echo while read in do echo in done 看来read命令将输入 中的反斜杠解释为转义符并删除它们 我需要逐行处理文件而不更改其内容 并且我不知道如何停止在这里智能读取 有任
  • 如何在 sed 中用“pwd”的结果替换令牌?

    我正在尝试做这样的事情 sed s REPLACE WITH PATH pwd 不幸的是 我出错了 sed e expression 1 char 23 unknown option to s 为什么会发生这种情况 您需要使用不同的字符而不
  • XAMPP Windows 上的 Php Cron 作业

    嗯 我是这个词的新手CRON 据我所知 这是一个Unix安排特定操作在定义的时间间隔后执行的概念 我需要运行一个php文件 每小时更新一次数据库 但我的困惑在于安排执行 我在用XAMPP用于 Windows 7 上的本地开发测试 我发现了什
  • 如何在 Linux/OS X 上温和地终止 Firefox 进程

    我正在使用 Firefox 进行一些自动化操作 尽管我可以从 shell 打开 Firefox 窗口 但我无法正确终止它 如果我kill火狐进程与kill 3 or kill 2当我下次打开新的 Firefox 窗口时 命令会询问我是否要在
  • 使用运算符 j 操作 zsh 数组

    以下代码摘自here http eseth org 2010 git in zsh html function vi git st local ahead behind remote local a gitstatus Are we on
  • 比较linux中的两个未排序列表,列出第二个文件中的唯一项

    我有 2 个包含号码列表 电话号码 的文件 我正在寻找一种列出第二个文件中第一个文件中不存在的数字的方法 我尝试过各种方法 comm getting some weird sorting errors fgrep v x f second
  • Bash 的源命令无法处理从互联网上卷曲的文件

    我正在尝试使用curl从互联网获取脚本文件 如下所示 source lt curl url echo done 我看到的是 完成 得到了回响before卷曲甚至开始下载文件 这是实际的命令和输出 bash 3 2 source lt cur
  • 如何使用 nohup 获取正在运行的程序列表

    我正在通过 SSH 连接访问运行 CentOS linux 发行版 的服务器 由于我无法始终保持登录状态 因此我使用 nohup command 来运行我的程序 我找不到如何获取我开始使用 nohup 的所有程序的列表 工作 只有在我注销之
  • Python 子进程:无法转义引号

    我知道以前曾问过类似的问题 但它们似乎都是通过重新设计参数的传递方式 即使用列表等 来解决的 但是 我这里有一个问题 因为我没有这个选项 有一个特定的命令行程序 我使用的是 Bash shell 我必须向其传递带引号的字符串 它不能不被引用
  • Ruby 在特定目录中运行 shell 命令

    我知道如何在 Ruby 中运行 shell 命令 例如 x cmd 但是 如何指定运行此命令的目录 有没有类似的脱壳方式 类似subprocess Popen在Python中 subprocess Popen r c mytool tool
  • SIGHUP 用于重新加载配置

    根据signal 7 SIGHUP用于检测控制终端的挂起或控制进程的死亡 然而 我遇到过很多 OSS 守护进程 服务 其中SIGHUP用于启动配置的重新加载 这里有一些例子 hostapd sshd snort etc 这是实现重新加载的标
  • 重新链接匿名(未链接但打开)文件

    在 Unix 中 可以创建匿名文件的句柄 例如 使用 creat 创建并打开它 然后使用 unlink 删除目录链接 留下一个带有 inode 和存储的文件 但没有可能的方法重新打开它 此类文件通常用作临时文件 通常这就是 tmpfile
  • shell脚本中是否有互斥/信号量机制?

    我正在 shell 脚本中寻找互斥 信号量 并发机制 考虑以下情况 除非 a 用户不关闭共享文件 否则 b 用户应该无法打开 更新它 我只是想知道如何在 shell 脚本中实现互斥量 信号量 临界区等 在 shell 脚本中实现锁定机制 文
  • PHP 日志文件颜色

    我正在编写一个 PHP 日志文件类 但我想为写入文件的行添加颜色 我遇到的问题是颜色也会改变终端的颜色 我想要实现的是仅更改写入日志文件的行的颜色 class logClass extends Singleton private funct
  • 用 C 实现 Unix shell:检查文件是否可执行

    我正在努力用 C 语言实现 Unix shell 目前正在处理相对路径的问题 特别是在输入命令时 现在 我每次都必须输入可执行文件的完整路径 而我宁愿简单地输入 ls 或 cat 我已经设法获取 PATH 环境变量 我的想法是在 字符处拆分

随机推荐