Here is an AWK one liner.
$ PATH=$(printf %s "$PATH" \ | awk -vRS=: -vORS= '!a[$0]++ {if (NR>1) printf(":"); printf("%s", $0) }' ) where:
printf %s "$PATH"prints the content of$PATHwithout a trailing newlineRS=:changes the input record delimiter character (default is newline)ORS=changes the output record delimiter to the empty stringathe name of an implicitly created array$0references the current recorda[$0]is a associative array dereference++is the post-increment operator!a[$0]++guards the right hand side, i.e. it makes sure that the current record is only printed, if it wasn't printed beforeNRthe current record number, starting with 1
That means that AWK is used to split the PATH content along the : delimiter characters and to filter out duplicate entries without modifying the order.
Since AWK associative arrays are implemented as hash tables the runtime is linear (i.e. in O(n)).
Note that we don't need look for quoted : characters because shells don't provide quoting to support directories with : in its name in the PATH variable.
awkAwk + paste
The above can be simplified with pastepaste:
$ PATH=$(printf %s "$PATH" | awk -v RS=vRS=: '!a[$0]++' | paste -s -d: -) The paste command is used to intersperse the awk output with colons. This simplifies the awk action to printing (which is the default action).
Python
The same as Python two-liner:
$ PATH=$(python3 -c 'import os; from collections import OrderedDict; \ l=os.environ["PATH"].split(":"); print(":".join(OrderedDict.fromkeys(l)))' )