Question

Il est typique d’avoir quelque chose comme ceci dans votre fichier cshrc pour définir le chemin:

set path = ( . $otherpath $path )

mais, le chemin est dupliqué lorsque vous sourcez votre fichier cshrc plusieurs fois, comment empêche-t-on la duplication?

EDIT: C’est une façon malpropre de le faire:

set localpaths = ( . $otherpaths )
echo ${path} | egrep -i "$localpaths" >& /dev/null
if ($status != 0) then
    set path = ( . $otherpaths $path )
endif
Était-ce utile?

La solution

vous pouvez utiliser le script Perl suivant pour supprimer les chemins des doublons.

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

J'espère que c'est utile.

Autres conseils

Je suis surpris que personne n'ait utilisé la tr ":" "\n" | grep -x technique pour rechercher si un dossier donné existe déjà dans $ PATH. Aucune raison de ne pas le faire?

En 1 ligne:

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

Voici une fonction que j’ai moi-même créée pour ajouter plusieurs dossiers à la fois à $ PATH (utilisez & "aaa: bbb: ccc &"; argument en tant qu’argument), en vérifiant chaque doublon avant d’ajouter:

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"
}

Il peut être appelé dans un script comme celui-ci:

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

Il présente les avantages suivants:

  • Pas de basismes ni de syntaxe spécifique au shell. Il fonctionne parfaitement avec !#/bin/sh (ive testé avec tiret)
  • Plusieurs dossiers peuvent être ajoutés à la fois
  • Aucun tri, conserve l'ordre des dossiers
  • traite parfaitement les espaces dans les noms de dossier
  • Un seul test fonctionne peu importe si $ folder est au début, à la fin, au milieu ou s'il est le seul dossier de $ PATH (évitant ainsi de tester x: *, *: x, : x: , x, comme beaucoup de solutions le font implicitement)
  • Fonctionne (et conserve) si $ PATH commence ou se termine par & ";:: &" ;, ou a & "; :: ::!!"; en elle (ce qui signifie dossier en cours)
  • Non awk ou sed nécessaire.
  • Respectueux de l'EPA;) La valeur IFS d'origine est conservée et toutes les autres variables sont locales à l'étendue de la fonction.

J'espère que ça aide!

ok, pas dans csh, mais voici comment j'ajoute $ HOME / bin à mon chemin d'accès en bash ...

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

assaisonner au goût ...

J'utilise le script suivant (Bourne / Korn / POSIX / Bash) depuis presque dix ans:

:   "@(#)$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;
}'

Dans Korn Shell, j'utilise:

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

Cela me laisse avec PATH contenant le nouveau répertoire et les autres répertoires bin au premier plan, plus une copie de chaque nom de répertoire dans la valeur du chemin principal, sauf que les répertoires bin et anciens ont été supprimés.

Vous devez adapter cela au shell C (désolé - mais je suis un fervent partisan des vérités énoncées à l'adresse Programmation en shell C considérée comme nuisible ). Avant tout, vous n’aurez pas à jouer avec le séparateur de côlon, la vie est en fait plus facile.

Eh bien, si l'ordre de vos chemins ne vous intéresse pas, vous pouvez faire quelque chose comme:

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

Cela triera vos chemins et supprimera tous les chemins supplémentaires identiques. Si tu as . dans votre chemin, vous voudrez peut-être le supprimer avec un grep -v et le rajouter à la fin.

Voici un long courrier sans tri:
set path = (echo $path | tr ' ' '\n' | perl -e 'while (<>) { print $_ unless $s{$_}++; }' | tr '\n' ' ')

dr_peper,

Je préfère généralement m'en tenir aux fonctionnalités de script du shell dans lequel je vis. Le rend plus portable. J'ai donc aimé votre solution utilisant les scripts csh. Je viens de l'étendre pour travailler par répertoire dans les répertoires locaux afin de le faire fonctionner moi-même.

foreach dir ( $localdirs )
    echo ${path} | egrep -i "$dir" >& /dev/null
    if ($status != 0) then
        set path = ( $dir $path )
    endif
end

Utilisation de sed (1) pour supprimer les doublons.

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

Ceci supprimera les doublons après la première instance, ce qui peut être ou ne pas être ce que vous voulez, par exemple:

$ 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
$

Profitez!

Voici ce que j'utilise - peut-être que quelqu'un le trouvera utile:

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

Je définis toujours mon chemin à partir de zéro dans .cshrc. C’est-à-dire que je commence par un chemin de base, quelque chose comme:

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

(selon le système).

Et ensuite faire:

set path = ($otherPath $path)

pour ajouter plus de choses

J'ai le même besoin que la question initiale. En me basant sur vos réponses précédentes, j’ai utilisé Korn / POSIX / Bash:

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

J'ai eu des difficultés à le traduire directement en csh (les règles d'échappement de csh sont insensées). J'ai utilisé (comme suggéré par dr_pepper):

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

Avez-vous des idées pour le simplifier davantage (réduire le nombre de tuyaux)?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top