使用 awk 命令删除重复的 $PATH 条目

我正在尝试编写一个 bash shell 函数,它肯定允许我从我的 PATH 环境变量中删除目录站点的复制副本。

有人告诉我,可以通过使用 awk 命令的单行命令来实现这一点,但我无法准确确定如何做到这一点。 有谁知道具体怎么做?

60
2022-06-07 14:37:22
资源 分享
答案: 9

我当然会使用 tr、type 和 uniq 等基本工具来完成:

NEW_PATH=`echo $PATH | tr ':' '\n' | sort | uniq | tr '\n' ':'`

如果您的路径中没有什么特别或奇怪的东西,它需要起作用

0
2022-07-14 00:19:32
资源

这是我的版本:

path_no_dup () 
{ 
    local IFS=: p=();

    while read -r; do
        p+=("$REPLY");
    done < <(sort -u <(read -ra arr <<< "$1" && printf '%s\n' "${arr[@]}"));

    # Do whatever you like with "${p[*]}"
    echo "${p[*]}"
}

用法: path_no_dup "$PATH"

样本输出:

rany$ v='a:a:a:b:b:b:c:c:c:a:a:a:b:c:a'; path_no_dup "$v"
a:b:c
rany$
1
2022-07-14 00:15:42
资源
PATH=`awk -F: '{for (i=1;i<=NF;i++) { if ( !x[$i]++ ) printf("%s:",$i); }}' <<< "$PATH"`

awk代码说明:

  1. 用冒号分隔输入
  2. 将新路径访问附加到关联数组以快速重复出现 - up
  3. 打印关联数组

为了增强突然性,这个单行器很快:awk 利用链式哈希表来实现分摊的 O (1) 性能。

基于 删除重复的 $PATH 条目

1
2022-07-09 22:21:06
资源
PATH=`perl -e 'print join ":", grep {!$h{$_}++} split ":", $ENV{PATH}'`
export PATH

这利用了 perl 并具有许多优点:

  1. 它删除重复项
  2. 它保持类型顺序
  3. 它保持最早的出现(/usr/bin:/sbin:/usr/bin 将导致 /usr/bin:/sbin
2
2022-06-08 06:24:39
资源

使用 awk 划分 : 上的路径,然后遍历每个字段并将其存储在数组中。 如果您遇到当前在阵列中的区域,则表示您过去确实见过它,因此不要发布它。

下面是一个例子:

$ MYPATH=.:/foo/bar/bin:/usr/bin:/foo/bar/bin
$ awk -F: '{for(i=1;i<=NF;i++) if(!($i in arr)){arr[$i];printf s$i;s=":"}}' <<< "$MYPATH"
.:/foo/bar/bin:/usr/bin

(更新以删除跟踪 :。)

0
2022-06-07 15:09:09
资源

关于这个 这里 也有类似的讨论。

我采取了一些不同的方法。 与仅批准从所有挂载的各种初始化文件中设置的 PATH 不同,我喜欢使用 getconf 来识别系统路径并首先对其进行区域划分,然后添加我喜欢的路径顺序,然后使用 awk 删除任何类型的比赛。 这可能会或可能不会真正加速命令的执行(理论上也更安全),但它给了我舒适的模糊感。

# I am entering my preferred PATH order here because it gets set,
# appended, reset, appended again and ends up in such a jumbled order.
# The duplicates get removed, preserving my preferred order.
#
PATH=$(command -p getconf PATH):/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
# Remove duplicates
PATH="$(printf "%s" "${PATH}" | /usr/bin/awk -v RS=: -v ORS=: '!($0 in a) {a[$0]; print}')"
export PATH

[~]$ echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/lib64/ccache:/usr/games:/home/me/bin
4
2022-06-07 15:07:25
资源

sed(下面使用 GNU sed 语法)也可以完成这项工作:

MYPATH=$(printf '%s\n' "$MYPATH" | sed ':b;s/:\([^:]*\)\(:.*\):\1/:\1\2/;tb')

这个只有在第一个路径是 . 的情况下才能正常工作,就像在 dogbane 中一样。

作为一个整体情况,您需要添加一个额外的 s 命令:

MYPATH=$(printf '%s\n' "$MYPATH" | sed ':b;s/:\([^:]*\)\(:.*\):\1/:\1\2/;tb;s/^\([^:]*\)\(:.*\):\1/:\1\2/')

它甚至适用于这样的建筑和施工:

$ echo "/bin:.:/foo/bar/bin:/usr/bin:/foo/bar/bin:/foo/bar/bin:/bar/bin:/usr/bin:/bin" \
| sed ':b;s/:\([^:]*\)\(:.*\):\1/:\1\2/;tb;s/^\([^:]*\)\(:.*\):\1/\1\2/'

/bin:.:/foo/bar/bin:/usr/bin:/bar/bin
2
2022-06-07 15:04:05
资源

这是一个精简的:

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 中没有包含“:”的目录,否则它将被搞砸。

一些功劳:

21
2022-06-07 15:02:12
资源

如果您在 PATH 中没有重复项,并且您只想添加不存在的目录,您可以单独使用 shell 快速完成。

for x in /path/to/add …; do
  case ":$PATH:" in
    *":$x:"*) :;; # already there
    *) PATH="$x:$PATH";;
  esac
done

下面是一个从 $PATH 中删除重复项的 shell 片段。 它一次通过一个访问,并且还复制那些尚未看到的访问。

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
45
2022-06-07 14:43:20
资源