Skip to main content
finish Gilles' edit, fixed some quoting, removed ACE vulnerability. Removed wrong usage of echo.
Source Link
Stéphane Chazelas
  • 586.8k
  • 96
  • 1.1k
  • 1.7k

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')" 

It simply splits on colon (split(/:/, scalar <>$ENV{PATH})), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# Deduplicate path variables get_var () { eval "echo'printf \$$1""%s\n" "${'"$1"'}"' } set_var () { eval "$1=\"$2\"""$1=\"\$2\"" } dedup_pathvar () { pathvar_name="$1" pathvar_value="$(get_var $pathvar_name"$pathvar_name")" deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")" set_var $pathvar_name"$pathvar_name" "$deduped_path" } dedup_pathvar PATH dedup_pathvar MANPATH 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')" 

It simply splits on colon (split(/:/, scalar <>)), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# Deduplicate path variables get_var () { eval "echo \$$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 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')" 

It simply splits on colon (split(/:/, $ENV{PATH})), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# 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 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).

simplify the one-liner and fix it breaking on spaces and backslashes and other characters
Source Link
Gilles 'SO- stop being evil'
  • 866.1k
  • 205
  • 1.8k
  • 2.3k

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(echo $PATH | perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, scalar <>$ENV{PATH}))')" 

It simply splits on colon (split(/:/, scalar <>)), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# Deduplicate path variables get_var () { eval "echo \$$1" } set_var () { eval "$1=\"$2\"" } dedup_pathvar () { pathvar_name="$1" pathvar_value="$(get_var $pathvar_name)" deduped_path="$(echo $pathvar_value | perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, scalar <>$ARGV[0]))' "$pathvar_value")" set_var $pathvar_name "$deduped_path" } dedup_pathvar PATH dedup_pathvar MANPATH 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(echo $PATH | perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, scalar <>))')" 

It simply splits on colon (split(/:/, scalar <>)), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# Deduplicate path variables get_var () { eval "echo \$$1" } set_var () { eval "$1=\"$2\"" } dedup_pathvar () { pathvar_name="$1" pathvar_value="$(get_var $pathvar_name)" deduped_path="$(echo $pathvar_value | perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, scalar <>))')" set_var $pathvar_name "$deduped_path" } dedup_pathvar PATH dedup_pathvar MANPATH 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')" 

It simply splits on colon (split(/:/, scalar <>)), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# Deduplicate path variables get_var () { eval "echo \$$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 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).

Source Link
Ryan C. Thompson
  • 5.5k
  • 6
  • 31
  • 24

Here's an intelligible one-liner solution that does all the right things: removes duplicates, preserves the ordering of paths, and doesn't add a colon at the end. So it should give you a deduplicated PATH that gives exactly the same behavior as the original:

PATH="$(echo $PATH | perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, scalar <>))')" 

It simply splits on colon (split(/:/, scalar <>)), uses uses grep { not $seen{$_}++ } to filter out any repeated instances of paths except for the first occurrence, and then joins the remaining ones back together separated by colons and prints the result (print join(":", ...)).

If you want some more structure around it, as well as the ability to deduplicate other variables as well, try this snippet, which I'm currently using in my own config:

# Deduplicate path variables get_var () { eval "echo \$$1" } set_var () { eval "$1=\"$2\"" } dedup_pathvar () { pathvar_name="$1" pathvar_value="$(get_var $pathvar_name)" deduped_path="$(echo $pathvar_value | perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, scalar <>))')" set_var $pathvar_name "$deduped_path" } dedup_pathvar PATH dedup_pathvar MANPATH 

That code will deduplicate both PATH and MANPATH, and you can easily call dedup_pathvar on other variables that hold colon-separated lists of paths (e.g. PYTHONPATH).