How to keep from duplicating path variable in csh

前端 未结 12 738
我在风中等你
我在风中等你 2020-11-27 05:19

It is typical to have something like this in your cshrc file for setting the path:

set path = ( . $otherpath $path )

but, the path gets dup

相关标签:
12条回答
  • 2020-11-27 05:56

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

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

    I had difficulties to translate it directly in csh (csh escape rules are insane). I have used (as suggested by dr_pepper):

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

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

    0 讨论(0)
  • 2020-11-27 06:01

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

    In 1 line:

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

    Here is a function ive made myself to add several folders at once to $PATH (use "aaa:bbb:ccc" notation as argument), checking each one 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 a script like this:

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

    It has the following advantages:

    • No bashisms or any shell-specific syntax. It run perfectly with !#/bin/sh (ive tested with dash)
    • Multiple folders can be added at once
    • No sorting, preserves folder order
    • Deals perfectly with spaces in folder names
    • A single test works no matter if $folder is at begginning, end, middle, or is the only folder in $PATH (thus avoiding testing x:*, *:x, :x:, x, as many of the solutions here implicitly do)
    • Works (and preserve) if $PATH begins or ends with ":", or has "::" in it (meaning current folder)
    • No awk or sed needed.
    • EPA friendly ;) Original IFS value is preserved, and all other variables are local to the function scope.

    Hope that helps!

    0 讨论(0)
  • 2020-11-27 06:04

    you can use the following Perl script to prune paths of duplicates.


    #!/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;
        }
    }
    

    i hope thats useful.

    0 讨论(0)
  • 2020-11-27 06:05

    Here's what I use - perhaps someone else will find it useful:

    #!/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
    #    J. P. 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";'
    
    0 讨论(0)
  • 2020-11-27 06:12

    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 the duplicates after the first instance, which may or may not be what you want, e.g.:

    $ 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; t a; s/::*/:/g; s/^://; s/:$//;'
    /bin:/usr/bin:/usr/local/bin
    $
    

    Enjoy!

    0 讨论(0)
  • 2020-11-27 06:15

    I've been using the following (Bourne/Korn/POSIX/Bash) script for most of a 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 Korn shell, I use:

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

    This leaves me with PATH containing the new and other bin directories at the front, plus one copy of each directory name in the main path value, except that the old and extra bin directories have bin removed.

    You would have to adapt this to C shell (sorry - but I'm a great believer in the truths enunciated at C Shell Programming Considered Harmful). Primarily, you won't have to fiddle with the colon separator, so life is actually easier.

    0 讨论(0)
提交回复
热议问题