Contents

从$路径变量中删除冗余路径

1. 概述

当我们想在 Linux 中执行一个程序时,shell 会在目录列表中搜索该程序。此列表存储在$PATH环境变量 中。在本教程中,我们将看到如何从*$PATH变量中删除路径。此外,我们将了解如何查找和删除$PATH*变量中的重复条目。

2. 问题介绍

当我们启动一个新的 Linux 会话时,系统用冒号分隔的目录列表填充*$PATH变量。我们可以使用printenv 命令查看$PATH变量*中存储的内容:

$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib/java/bin:/usr/lib/java/jre/bin:/usr/lib64/qt5/bin

如我们所见,该*$PATH变量中有几个目录。我们还可以注意到,Java 将自己的目录添加到了$PATH变量中。此外,我们可以看到目录/usr/lib64/qt5/bin是重复的。可能存在我们不想在$PATH*变量中包含某些目录的情况。例如,我们可能想要删除 Java 的目录。

在接下来的部分中,我们将看到我们可以删除不需要的目录。或者,我们还将编写一个名为remove_from_path的 bash 函数,它接收一个目录作为参数并将其从*$PATH变量中删除。此外,我们可能想要制作$PATH*通过删除重复的条目,变量更短且更易于阅读。

因此,我们将编写另一个名为remove_duplicates_from_path的 bash 函数,该函数删除重复的条目,只留下每个目录的一个实例。

3. 如何删除路径

从*$PATH*变量中删除路径有不同的选择。我们可以使用一个或另一个,这取决于我们想要一个永久或临时的解决方案。

3.1. 永久删除它

如果我们想永久删除路径,我们可以修改将目录添加到$PATH变量的脚本。*通常,脚本/etc/profile使用一些基本目录(如/usr/bin* )设置*$PATH变量。此外,/etc/ profile.d目录中的脚本有时还会将路径添加到$PATH*变量。

最后,每个 Linux 用户都可以拥有脚本 ~/.profile、~/.bash_profile 和 ~/.bashrc,它们还可以将目录添加到*$PATH变量中。**如果我们在任何这些脚本中找到路径并对其进行修改,我们可以永久删除该路径。*例如,我们可以在/etc/profile.d/jdk.sh脚本:

export JAVA_HOME=/usr/lib/java
export PATH="${PATH}:${JAVA_HOME}/bin:${JAVA_HOME}/jre/bin"

请注意,最后一行将 Java 的目录添加到$PATH变量中。*我们可以注释或删除/etc/profile.d/jdk.sh脚本的最后一行,以从$PATH*变量中永久删除 Java 的路径。从文件中删除路径后,当用户启动新会话时, $PATH变量中将没有该目录。

3.2. 手动临时删除

如果我们想暂时删除路径,我们可以用新值覆盖$PATH变量。*但是,临时解决方案仅适用于当前用户会话。我们可以打印$PATH变量的内容,分析我们要保留哪些路径以及要删除哪些路径,然后使用新的路径列表设置$PATH*。让我们更改*$PATH变量以删除java* 命令的路径:

$ which java
/usr/lib/java/bin/java
$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib/java/bin:/usr/lib/java/jre/bin:/usr/lib64/qt5/bin
$ PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib64/qt5/bin
$ which java
which: no java in (/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib64/qt5/bin)

在这种情况下,我们首先测试 Bash 是否可以使用which 命令找到java命令。然后,我们从*$PATH变量中删除了 Java 的目录。最后,**我们再次使用which*命令来验证该命令不再存在。**

3.3. 编写函数

前一种方法涉及一些手动工作,如果我们想删除 bash 脚本中的路径,这是不可能的。因此,我们可以编写一个函数以编程方式从***$PATH*变量中删除一个目录。**

这样,我们可以轻松地在脚本中使用该函数。此外,我们可以接收要删除的目录作为参数。如果我们遵循手动方法的相同想法,我们可以使用for循环遍历*$PATH*变量,检查每个路径。$IFS 特殊变量可以帮助我们遍历目录列表。

如果我们将*$IFS设置为冒号字符,那么for循环将使用该字符来拆分字符串。如果我们改变它,我们应该将$IFS恢复到它的原始值。让我们编写remove_from_path*函数来删除参数接收到的路径:

$ remove_from_path()
{
    DIR=$1
    NEWPATH=
    OLD_IFS=$IFS
    IFS=:
    for p in $PATH; do
        if [ $p != $DIR ]; then
            NEWPATH=${NEWPATH:+$NEWPATH:}$p
        fi
    done
    IFS=$OLD_IFS
    PATH=$NEWPATH
}

正如我们所见,函数很简单。我们将要删除的目录存储在*$DIR变量中,并在$IFS中将冒号字符设置为分隔符后遍历$PATH列表。我们从一个空变量$NEWPATH* 开始,在每次循环迭代中,我们决定是否要将路径附加到*$NEWPATH*。

最后,我们将*$IFS变量恢复为其原始值,并将$PATH变量替换为$NEWPATH*。我们使用*${NEWPATH:+$NEWPATH:}表示法。这是一个参数扩展 这个符号有两个部分,NEWPATH:+$新路径:。shell 写入$NEWPATH,只有当$NEWPATH不为空时才使用冒号。如果$NEWPATH*为空,shell 会留下一个空字符串。如果还没有任何路径,我们使用这种表示法来避免添加冒号。让我们测试它以删除 Java 的目录:

$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib/java/bin:/usr/lib/java/jre/bin:/usr/lib64/qt5/bin
$ remove_from_path /usr/lib/java/bin
$ remove_from_path /usr/lib/java/jre/bin
$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib64/qt5/bin

如我们所见,我们使用remove_from_path函数删除了两个 Java 目录。

3.4. 使用单线

最后,我们的remove_from_path函数有一个单行替代方案。**我们可以使用trgreppaste 的组合来拆分、删除和连接路径。**让我们来看看:

$ PATH=$(tr : '\n' <<<"$PATH" | grep -x -v "/usr/lib/java/bin" | paste -sd:)

我们可以看到这个函数有三个步骤,我们将结果存储在*$PATH变量中。首先,我们使用tr将冒号替换为**$PATH*中的换行符。

然后,我们使用grep删除不需要的目录。我们可以注意到我们使用了*-x-v参数。我们使用-x来告诉grep仅在整行匹配时才匹配。我们使用-v来反转结果,因此grep*删除匹配的行。

最后,我们将grep的结果与paste连接起来,并将分隔符设置为冒号字符。这样,我们重新创建*$PATH变量没有我们要删除的路径。如果需要,**我们还可以将单行代码包装在一个函数中,并将目录作为参数接收。**让我们重写我们的remove_from_path*函数:

$ remove_from_path() {
    DIR=$1
    PATH=$(tr : '\n' <<<"$PATH" | grep -x -v "$DIR" | paste -sd:)
}

让我们用它来删除 Java 的目录:

$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib/java/bin:/usr/lib/java/jre/bin:/usr/lib64/qt5/bin
$ remove_from_path /usr/lib/java/bin
$ remove_from_path /usr/lib/java/jre/bin
$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib64/qt5/bin

4. 如何查找和删除重复的路径

*在我们的$PATH示例中,/usr/lib64/qt5/bin目录是重复的。这不是错误,但多次使用相同的路径是多余的。*因此,我们可能想要清理$PATH变量并删除重复的目录。

**我们可以编写一个函数来以编程方式查找和删除重复的目录。**要在函数中查找重复条目,我们可以循环路径列表并仅在尚未添加路径时复制每个路径。要知道我们是否已经添加了路径,我们可以使用关联数组 并将我们循环的每个路径存储为该数组中的索引。

所以,我们知道如果它还不是索引,我们还没有添加路径。让我们写remove_duplicates_from_path函数:

$ remove_duplicates_from_path() {
    OLD_IFS=$IFS
    IFS=:
    NEWPATH=
    unset EXISTS
    declare -A EXISTS
    for p in $PATH; do
        if [ -z ${EXISTS[$p]} ]; then
            NEWPATH=${NEWPATH:+$NEWPATH:}$p
            EXISTS[$p]=yes
        fi
    done
    IFS=$OLD_IFS
    PATH=$NEWPATH
}

这个函数类似于我们的第一个remove_from_path函数。我们使用*$IFS使用冒号字符拆分$PATH* ,并使用*$NEWPATH*构建新路径。

最后,我们将*$IFS恢复为其原始值并将$PATH*设置为新路径列表。

但是,我们还添加了*$EXISTS数组。首先,我们取消设置它以清除任何先前的值。然后,我们使用declare -A EXISTS*将其声明为关联数组。这允许我们使用字符串作为索引。

要查看路径是否已经存在,我们使用*[ -z ${EXISTS[$p]} ]。如果$EXISTS$p索引。由于$p*变量是迭代中的当前目录,我们正在测试我们之前是否已经看到过路径。

如果这是我们第一次看到路径,我们将其添加到*$NEWPATH中,使用冒号分隔值。然后,我们将$p作为索引添加到$EXISTS*数组,因此我们知道我们已经添加了该路径。让我们测试一下这个功能:

$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib/java/bin:/usr/lib/java/jre/bin:/usr/lib64/qt5/bin
$ remove_duplicates_from_path 
$ printenv PATH
/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib64/qt5/bin:/usr/lib/java/bin:/usr/lib/java/jre/bin

** /usr/lib64/qt5/bin路径在我们调用remove_duplicates_from_path后不再重复。**