How to avoid duplicate path variable in csh

Typically, the cshrc file has something like this:

set path = ( . $otherpath $path ) 

but the path is duplicated when you send the cshrc file several times, how do you prevent duplication?

EDIT: This is one unclean way to do this:

 set localpaths = ( . $otherpaths ) echo ${path} | egrep -i "$localpaths" >& /dev/null if ($status != 0) then set path = ( . $otherpaths $path ) endif 
+8
path environment-variables csh path-variables
Sep 25 '08 at 20:16
source share
11 answers

you can use the following Perl script to trim duplicate paths.




 #!/usr/bin/perl # # ^^ ensure this is pointing to the correct location. # # Title: SLimPath # Author: David "Shoe Lace" Pyke <eselle@users.sourceforge.net > # : Tim Nelson # Purpose: To create a slim version of my envirnoment path so as to eliminate # duplicate entries and ensure that the "." path was last. # Date Created: April 1st 1999 # Revision History: # 01/04/99: initial tests.. didn't wok verywell at all # : retreived path throught '$ENV' call # 07/04/99: After an email from Tim Nelson <wayland@ne.com.au> got it to # work. # : used 'push' to add to array # : used 'join' to create a delimited string from a list/array. # 16/02/00: fixed cmd-line options to look/work better # 25/02/00: made verbosity level-oriented # # use Getopt::Std; sub printlevel; $initial_str = ""; $debug_mode = ""; $delim_chr = ":"; $opt_v = 1; getopts("v:hd:l:e:s:"); OPTS: { $opt_h && do { print "\n$0 [-v level] [-d level] [-l delim] ( -e varname | -s strname | -h )"; print "\nWhere:"; print "\n -h This help"; print "\n -d Debug level"; print "\n -l Delimiter (between path vars)"; print "\n -e Specify environment variable (NB: don't include \$ sign)"; print "\n -s String (ie. $0 -s \$PATH:/looser/bin/)"; print "\n -v Verbosity (0 = quiet, 1 = normal, 2 = verbose)"; print "\n"; exit; }; $opt_d && do { printlevel 1, "You selected debug level $opt_d\n"; $debug_mode = $opt_d; }; $opt_l && do { printlevel 1, "You are going to delimit the string with \"$opt_l\"\n"; $delim_chr = $opt_l; }; $opt_e && do { if($opt_s) { die "Cannot specify BOTH env var and string\n"; } printlevel 1, "Using Environment variable \"$opt_e\"\n"; $initial_str = $ENV{$opt_e}; }; $opt_s && do { printlevel 1, "Using String \"$opt_s\"\n"; $initial_str = $opt_s; }; } if( ($#ARGV != 1) and !$opt_e and !$opt_s){ die "Nothing to work with -- try $0 -h\n"; } $what = shift @ARGV; # Split path using the delimiter @dirs = split(/$delim_chr/, $initial_str); $dest; @newpath = (); LOOP: foreach (@dirs){ # Ensure the directory exists and is a directory if(! -e ) { printlevel 1, "$_ does not exist\n"; next; } # If the directory is ., set $dot and go around again if($_ eq '.') { $dot = 1; next; } # if ($_ ne `realpath $_`){ # printlevel 2, "$_ becomes ".`realpath $_`."\n"; # } undef $dest; #$_=Stdlib::realpath($_,$dest); # Check for duplicates and dot path foreach $adir (@newpath) { if($_ eq $adir) { printlevel 2, "Duplicate: $_\n"; next LOOP; }} push @newpath, $_; } # Join creates a string from a list/array delimited by the first expression print join($delim_chr, @newpath) . ($dot ? $delim_chr.".\n" : "\n"); printlevel 1, "Thank you for using $0\n"; exit; sub printlevel { my($level, $string) = @_; if($opt_v >= $level) { print STDERR $string; } } 



Hope this is helpful.

+3
Sep 25 '08 at 20:28
source share

Im surprised nobody used the tr ":" "\n" | grep -x technique tr ":" "\n" | grep -x tr ":" "\n" | grep -x to search if the given folder already exists in $ PATH. Any reason not for?

In 1 line:

 if ! $(echo "$PATH" | tr ":" "\n" | grep -qx "$dir") ; then PATH=$PATH:$dir ; fi 

Here the ive function forced itself to add several folders at once in $ PATH (use the argument "aaa: bbb: ccc" as an argument), checking each of them for duplicates before adding:

 append_path() { local SAVED_IFS="$IFS" local dir IFS=: for dir in $1 ; do if ! $( echo "$PATH" | tr ":" "\n" | grep -qx "$dir" ) ; then PATH=$PATH:$dir fi done IFS="$SAVED_IFS" } 

It can be called in the script as follows:

 append_path "/test:$HOME/bin:/example/my dir/space is not an issue" 

It has the following advantages:

  • No bugs or shell-specific syntax. It works fine with !#/bin/sh (ive checked dash)
  • Multiple folders can be added at once.
  • No sorting, folder order maintained
  • Ideal for spaces in folder names
  • One test works whether the $ folder is at the beginning, end, middle, or the only folder in $ PATH (thus avoiding testing x: *, *: x ,: x :, x, since many of solutions are implicit here)
  • It works (and is saved) if $ PATH begins or ends with the ":" symbol or has "::" in it (which means the current folder)
  • No awk or sed .
  • EPA friendly;) The original IFS value is preserved, and all other variables are local to the function area.

Hope this helps!

+13
Feb 19 2018-11-11T00:
source share

ok, not in csh, but here is how I add $ HOME / bin to my path in bash ...

 case $PATH in *:$HOME/bin | *:$HOME/bin:* ) ;; *) export PATH=$PATH:$HOME/bin esac 

season to taste ...

+4
Sep 25 '08 at 21:07
source share

I have been using the following (Bourne / Korn / POSIX / Bash) script for most of the decade:

 : "@(#)$Id: clnpath.sh,v 1.6 1999/06/08 23:34:07 jleffler Exp $" # # Print minimal version of $PATH, possibly removing some items case $# in 0) chop=""; path=${PATH:?};; 1) chop=""; path=$1;; 2) chop=$2; path=$1;; *) echo "Usage: `basename $0 .sh` [$PATH [remove:list]]" >&2 exit 1;; esac # Beware of the quotes in the assignment to chop! echo "$path" | ${AWK:-awk} -F: '# BEGIN { # Sort out which path components to omit chop="'"$chop"'"; if (chop != "") nr = split(chop, remove); else nr = 0; for (i = 1; i <= nr; i++) omit[remove[i]] = 1; } { for (i = 1; i <= NF; i++) { x=$i; if (x == "") x = "."; if (omit[x] == 0 && path[x]++ == 0) { output = output pad x; pad = ":"; } } print output; }' 

In the Korn shell, I use:

 export PATH=$(clnpath /new/bin:/other/bin:$PATH /old/bin:/extra/bin) 

This leaves me with a PATH containing the new and other bin directories in front, plus one copy of each directory name in the main path value, except that the old and additional bin directories are deleted by bin.

You will need to adapt this to the C shell (sorry - but I really believe in the truths proclaimed in C Shell Programming is considered harmful ). First of all, you don’t have to bother with a colon separator, so life is actually easier.

+2
Sep 26 '08 at 6:03
source share

Well, if you don't care what order your paths are in, you can do something like:

 set path=(`echo $path | tr ' ' '\n' | sort | uniq | tr '\n' ' '`) 

This sorts your paths and removes any additional paths that are the same. If you have. in your path, you can remove it with grep -v and re-add it to the end.

+2
Sep 29 '08 at 13:46
source share

Here is a long single line without sorting:
set path = ( echo $path | tr ' ' '\n' | perl -e 'while (<>) { print $_ unless $s{$_}++; }' | tr '\n' ' ' )

+2
Sep 30 '08 at 20:38
source share

Using sed (1) to remove duplicates.

 $ PATH=$(echo $PATH | sed -e 's/$/:/;s/^/:/;s/:/::/g;:a;s#\(:[^:]\{1,\}:\)\(.*\)\1#\1\2#g;ta;s/::*/:/g;s/^://;s/:$//;') 

This will remove duplicates after the first instance, which may or may not be what you want, for example:

 $ NEWPATH=/bin:/usr/bin:/bin:/usr/local/bin:/usr/local/bin:/bin $ echo $NEWPATH | sed -e 's/$/:/; s/^/:/; s/:/::/g; :a; s#\(:[^:]\{1,\}:\)\(.*\)\1#\1\2#g; ta; s/::*/:/g; s/^://; s/:$//;' /bin:/usr/bin:/usr/local/bin $ 

Enjoy it!

+2
Jan 28 2018-12-12T00:
source share

dr_peper,

I usually prefer to use the scripting capabilities of the shell in which I live. Makes it more portable. So, I liked your solution using csh scripts. I just expanded it to work on per dir in localdirs to make it work for myself.

 foreach dir ($ localdirs)
     echo $ {path} |  egrep -i "$ dir"> & / dev / null
     if ($ status! = 0) then
         set path = ($ dir $ path)
     endif
 end
+1
Nov 19 '09 at 5:26
source share

Here is what I use - maybe it will be useful to someone:

 #!/bin/csh # ABSTRACT # /bin/csh function-like aliases for manipulating environment # variables containing paths. # # BUGS # - These *MUST* be single line aliases to avoid parsing problems apparently related # to if-then-else # - Aliases currently perform tests in inefficient in order to avoid parsing problems # - Extremely fragile - use bash instead!! # # AUTHOR # JP Abelanet - 11/11/10 # Function-like alias to add a path to the front of an environment variable # containing colon (':') delimited paths, without path duplication # # Usage: prepend_path ENVVARIABLE /path/to/prepend alias prepend_path \ 'set arg2="\!:2"; if ($?\!:1 == 0) setenv \!:1 "$arg2"; if ($?\!:1 && $\!:1 !~ {,*:}"$arg2"{:*,}) setenv \!:1 "$arg2":"$\!:1";' # Function-like alias to add a path to the back of any environment variable # containing colon (':') delimited paths, without path duplication # # Usage: append_path ENVVARIABLE /path/to/append alias append_path \ 'set arg2="\!:2"; if ($?\!:1 == 0) setenv \!:1 "$arg2"; if ($?\!:1 && $\!:1 !~ {,*:}"$arg2"{:*,}) setenv \!:1 "$\!:1":"$arg2";' 
+1
Feb 04 '14 at 22:02
source share

I always set my path from scratch in .cshrc. That is, I start with the main path, something like:

 set path = (. ~/bin /bin /usr/bin /usr/ucb /usr/bin/X11) 

(depending on the system).

And then do:

 set path = ($otherPath $path) 

add more things

0
Sep 25 '08 at 20:20
source share

I have the same need as the original question. Based on your previous answers, I used in Korn / POSIX / Bash:

 export PATH=$(perl -e 'print join ":", grep {!$h{$_}++} split ":", "'$otherpath:$PATH\") 

It was difficult for me to translate it directly to csh (csh escape rules are insane). I used (as suggested by dr_pepper):

 set path = ( `echo $otherpath $path | tr ' ' '\n' | perl -ne 'print $_ unless $h{$_}++' | tr '\n' ' '`) 

Do you have any ideas to simplify it (reduce the number of pipes)?

0
Feb 18 '15 at 13:04
source share



All Articles