2008-09-25 12 views
8

Es típico tener algo como esto en su archivo cshrc para definir la ruta:Cómo evitar la duplicación de variable de ruta en CSH

set path = (. $otherpath $path) 

pero, el camino se duplica cuando fuente de su archivo cshrc varias veces ¿Cómo previene la duplicación?

EDIT: Esta es una forma impura de hacerlo:

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

Relacionados (aunque la mayoría de las respuestas de shell bourne): http://stackoverflow.com/questions/273909/how-do-i-manipulate-path-elements-in-shell-scripts – dmckee

+1

Debería publicar su método como una tarea separada respuesta, no como una edición de pregunta. –

Respuesta

3

puede utilizar el siguiente script en Perl para podar caminos de duplicados.


#!/usr/bin/perl 
# 
# ^^ ensure this is pointing to the correct location. 
# 
# Title: SLimPath 
# Author: David "Shoe Lace" Pyke <[email protected] > 
# : 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 <[email protected]> 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; 
    } 
} 

espero que eso es útil.

+0

oops ... no me di cuenta de que había dejado todos mis comentarios en él ... está todo bien. (Por cierto, otra vez Tim) :) – ShoeLace

0

siempre puse mi camino desde cero en cshrc. Eso es lo empezar con un camino básico, algo así como:

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

(dependiendo del sistema).

y luego hacer:

set path = ($otherPath $path) 

para añadir más cosas

+0

Perderá (cualquier actualización de) la configuración del sistema. – musiphil

4

bien, no en CSH, pero así es como añado $ HOME/bin a mi camino en bash ...

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

sazonar al gusto ...

2

tengo estado usando el siguiente secuencia de comandos (Bourne/Korn/POSIX/Bash) para la mayor parte de una década:

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

En Korn shell, utilizo:

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

Esto me deja con PATH que contiene los directorios nuevos y otros bin en la parte delantera, más una copia de cada nombre de directorio en el valor de la ruta principal, excepto que los directorios bin viejos y adicionales han eliminado bin.

Tendría que adaptar esto a C shell (lo siento, pero soy un gran creyente en las verdades enunciadas en C Shell Programming Considered Harmful). En primer lugar, no tendrá que jugar con el separador de colon, por lo que la vida es realmente más fácil.

2

Bueno, si no le importa qué orden sus caminos se encuentran, usted podría hacer algo como:

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

Eso va a clasificar sus caminos y eliminar cualquier caminos adicionales que son el mismo. Si usted tiene . en su camino, es posible que desee eliminarlo con grep -v y volver a agregarlo al final.

+0

El único problema que veo con esto es que alterará el orden de la ruta, pero me gusta el hecho de que es un trazador de líneas. –

2

Aquí es un largo de una sola línea sin clasificar:.
camino set = (echo $path | tr ' ' '\n' | perl -e 'while (<>) { print $_ unless $s{$_}++; }' | tr '\n' ' ')

1

dr_peper,

que prefiero generalmente se adhieren a las capacidades de scripting de la cáscara vivo en Hace que sea más portátil. Por lo tanto, me gustó tu solución usando scripts csh. Acabo de extenderlo para que funcione en cada dir en localdirs para que funcione por mí mismo.

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

Me sorprende que nadie utiliza siguiendo la técnica tr ":" "\n" | grep -x para buscar si una carpeta determinada ya existe en $ PATH. ¿Alguna razón para no hacerlo?

En 1 línea:

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

Aquí es un ive función hizo que yo pueda añadir varias carpetas a la vez a $ PATH (uso "aaa: bbb: ccc" notación como argumento), la comprobación de cada uno de los duplicados antes de añadir:

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

puede ser llamado en un script como el siguiente:

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

Tiene la Follo Ventajas del ala:

  • Sin bashisms ni ninguna sintaxis específica del shell. Que funcione perfectamente con !#/bin/sh (IVE probado con guión)
  • múltiples carpetas se pueden agregar a la vez
  • No clasificado, preserva el orden carpeta
  • Ofertas perfectamente a los espacios en los nombres de carpetas
  • Una sola prueba funciona, no importa si $ folder es al principio, al final, al medio o es la única carpeta en $ PATH (evitando así probar x: *, *: x, : x:, x, como muchas de las soluciones implícitamente lo hacen)
  • Funciona (y preserva) si $ PATH comienza o termina con ":", o tiene "::" en él (es decir, la carpeta actual)
  • No awk o sed necesario.
  • Compatible con la EPA;) El valor IFS original se conserva y todas las demás variables son locales para el alcance de la función.

Espero que ayude!

+0

+! Esto es esencialmente lo que he estado haciendo, ¡y funciona genial! –

2

Usando sed (1) para eliminar duplicados.

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

Esto eliminará los duplicados después de la primera instancia, que puede o no puede ser lo que usted quiere, por ejemplo .:

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

1

Esto es lo que yo uso - tal vez alguien más va a encontrar útil:

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

tengo la misma necesidad que la pregunta original. Sobre la base de sus respuestas anteriores, he utilizado en Korn/POSIX/Bash:

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

que tenía dificultades para traducirlo directamente en CSH (CSH reglas de escape están locos). He utilizado (según lo sugerido por dr_pepper):

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

¿Tiene ideas para simplificar más (reducir el número de tubos)?

Cuestiones relacionadas