我正在尝试编写一个 bash shell 函数,该函数允许我从 PATH 环境变量中删除目录的重复副本。
有人告诉我,可以使用该命令通过一行命令来实现这一目标awk
,但我不知道如何做到这一点。有人知道怎么做吗?
答案1
如果您在 中还没有重复项,PATH
并且只想添加尚不存在的目录,则可以仅使用 shell 轻松完成此操作。
for x in /path/to/add …; do
case ":$PATH:" in
*":$x:"*) :;; # already there
*) PATH="$x:$PATH";;
esac
done
这是一个从 中删除重复项的 shell 片段$PATH
。它会逐一浏览条目,并复制那些尚未看过的条目。
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*} # the first remaining entry
case $PATH: in
*:"$x":*) ;; # already there
*) PATH=$PATH:$x;; # not there yet
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:}
unset old_PATH x
fi
答案2
这是一个易懂的单行解决方案可以完成所有正确的事情:删除重复项,保留路径的顺序,并且不在末尾添加冒号。因此,它应该为您提供一个经过重复数据删除的 PATH,其行为与原始路径完全相同:
PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"
它只是在冒号 ( split(/:/, $ENV{PATH})
) 上进行拆分,使用 usegrep { not $seen{$_}++ }
过滤掉除第一次出现之外的任何重复路径实例,然后将剩余的路径重新连接在一起,并用冒号分隔并打印结果 ( print join(":", ...)
)。
如果您想要更多的结构,以及删除其他变量重复的能力,请尝试这个片段,我目前在我自己的配置中使用它:
# Deduplicate path variables
get_var () {
eval 'printf "%s\n" "${'"$1"'}"'
}
set_var () {
eval "$1=\"\$2\""
}
dedup_pathvar () {
pathvar_name="$1"
pathvar_value="$(get_var "$pathvar_name")"
deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")"
set_var "$pathvar_name" "$deduped_path"
}
dedup_pathvar PATH
dedup_pathvar MANPATH
该代码将对 PATH 和 MANPATH 进行重复数据删除,并且您可以轻松调用保存dedup_pathvar
以冒号分隔的路径列表的其他变量(例如 PYTHONPATH)。
答案3
这是一个时尚的:
printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'
更长(看看它是如何工作的):
printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'
好的,由于您是 Linux 新手,因此以下是如何实际设置不带尾随“:”的 PATH
PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`
顺便说一句,请确保您的 PATH 中没有包含“:”的目录,否则会弄乱。
一些功劳:
答案4
只要我们添加非 awk oneliners:
PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")
(可能很简单,PATH=$(zsh -fc 'typeset -U path; echo $PATH')
但 zsh 总是读取至少一个zshenv
配置文件,该文件可以修改PATH
。)
它使用了两个很好的 zsh 功能:
- 与数组关联的标量 (
typeset -T
) - 以及自动删除重复值的数组 (
typeset -U
)。