web-dev-qa-db-fra.com

Quel est le moyen le plus élégant de supprimer un chemin de la variable $ PATH dans Bash?

Ou plus généralement, comment puis-je supprimer un élément d'une liste séparée par deux-points dans une variable d'environnement Bash?

Je pensais avoir trouvé un moyen simple de faire cela il y a quelques années, en utilisant les formes plus avancées d'expansion variable de Bash, mais si c'est le cas, je l'ai perdue. Une recherche rapide sur Google a révélé étonnamment peu de résultats pertinents et aucun que j'appellerais "simple" ou "élégant". Par exemple, deux méthodes utilisant sed et awk, respectivement:

PATH=$(echo $PATH | sed -e 's;:\?/home/user/bin;;' -e 's;/home/user/bin:\?;;')
PATH=!(awk -F: '{for(i=1;i<=NF;i++){if(!($i in a)){a[$i];printf s$i;s=":"}}}'<<<$PATH)

Rien de simple n'existe? Y at-il quelque chose d'analogue à une fonction split () dans Bash?

Mettre à jour:
Il semble que je doive m'excuser pour ma question intentionnellement vague; J'étais moins intéressé par la résolution d'un cas d'utilisation spécifique que par le fait de provoquer une bonne discussion. Heureusement, je l'ai eu!

Il existe des techniques très intelligentes ici. En fin de compte, j'ai ajouté les trois fonctions suivantes à ma boîte à outils. La magie opère dans path_remove, qui repose en grande partie sur l'utilisation intelligente par Martin York de la variable RS de awk.

path_append ()  { path_remove $1; export PATH="$PATH:$1"; }
path_prepend () { path_remove $1; export PATH="$1:$PATH"; }
path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; }

Le seul véritable problème réside dans l'utilisation de sed pour supprimer le côlon final. Considérant à quel point le reste de la solution de Martin est simple, je suis tout à fait disposé à vivre avec elle!


Question connexe:  Comment manipuler des éléments $ PATH dans des scripts Shell?

93
Ben Blank

Une minute avec awk:

# Strip all paths with SDE in them.
#
export PATH=`echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}'`

Edit: Il répond aux commentaires ci-dessous:

$ export a="/a/b/c/d/e:/a/b/c/d/g/k/i:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i"
$ echo ${a}
/a/b/c/d/e:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i

## Remove multiple (any directory with a: all of them)
$ echo ${a} | awk -v RS=: -v ORS=: '/a/ {next} {print}'
## Works fine all removed

## Remove multiple including last two: (any directory with g)
$ echo ${a} | awk -v RS=: -v ORS=: '/g/ {next} {print}'
/a/b/c/d/e:/a/b/c/d/f:
## Works fine: Again!

Modifier en réponse à un problème de sécurité: (cela ne concerne pas la question)

export PATH=$(echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}' | sed 's/:*$//')

Cela supprime les points-virgules laissés en supprimant les dernières entrées, ce qui ajouterait effectivement . à votre chemin.

40
Martin York

Mon sale bidouille:

echo ${PATH} > t1
vi t1
export PATH=$(cat t1)
51
Martin York

Puisque le problème majeur de la substitution concerne les cas finaux, pourquoi ne pas faire en sorte que les cas finaux ne soient pas différents des autres cas? Si le chemin d'accès comportait déjà des deux points au début et à la fin, nous pourrions simplement rechercher la chaîne souhaitée entourée de deux points. En l'état, nous pouvons facilement ajouter ces deux-points et les supprimer ensuite.

# PATH => /bin:/opt/a dir/bin:/sbin
WORK=:$PATH:
# WORK => :/bin:/opt/a dir/bin:/sbin:
REMOVE='/opt/a dir/bin'
WORK=${WORK/:$REMOVE:/:}
# WORK => :/bin:/sbin:
WORK=${WORK%:}
WORK=${WORK#:}
PATH=$WORK
# PATH => /bin:/sbin

Pure bash :).

37
Andrew Aylett

Voici la solution la plus simple que je puisse imaginer:

#!/bin/bash
IFS=:
# convert it to an array
t=($PATH)
unset IFS
# perform any array operations to remove elements from the array
t=(${t[@]%%*usr*})
IFS=:
# output the new array
echo "${t[*]}"

L'exemple ci-dessus supprimera tout élément de $ PATH contenant "usr". Vous pouvez remplacer "* usr *" par "/ home/user/bin" pour ne supprimer que cet élément.

update per sschuberth

Même si je pense que les espaces dans un $PATH sont une idée de horrible, voici une solution qui le gère:

PATH=$(IFS=':';t=($PATH);n=${#t[*]};a=();for ((i=0;i<n;i++)); do p="${t[i]%%*usr*}"; [ "${p}" ] && a[i]="${p}"; done;echo "${a[*]}");

ou

IFS=':'
t=($PATH)
n=${#t[*]}
a=()
for ((i=0;i<n;i++)); do
  p="${t[i]%%*usr*}"
  [ "${p}" ] && a[i]="${p}"
done
echo "${a[*]}"
24
nicerobot

Voici une ligne qui, malgré les réponses acceptées et mieux notées actuelles, n'ajoute pas de caractères invisibles à PATH et peut traiter les chemins contenant des espaces:

export PATH=$(p=$(echo $PATH | tr ":" "\n" | grep -v "/cygwin/" | tr "\n" ":"); echo ${p%:})

Personnellement, je trouve aussi cela facile à lire/à comprendre, et cela n’implique que des commandes communes au lieu d’utiliser awk.

10
sschuberth

Voici une solution qui:

  • est pur Bash,
  • n'invoque pas d'autres processus (comme 'sed' ou 'awk'),
  • ne change pas IFS,
  • ne divise pas un sous-shell,
  • gère les chemins avec des espaces, et
  • supprime toutes les occurrences de l'argument dans PATH.

     removeFromPath () {
     local p d 
     p = ": $ 1:" 
     d = ": $ PATH:" 
     d = $ {d // $ p /:Name
     d = $ {d /#:/urther
     PATH = $ {d /%:/urther
    }
8
robinbb

La meilleure option pure bash que j'ai trouvée jusqu'à présent est la suivante:

function path_remove {
  PATH=${PATH/":$1"/} # delete any instances in the middle or at the end
  PATH=${PATH/"$1:"/} # delete any instances at the beginning
}

Ceci est basé sur la réponse pas tout à fait correcte à Ajouter le répertoire à $ PATH si ce n'est déjà fait sur le superutilisateur.

6
Mark Booth

fonction __path_remove () {
local D = ": $ {PATH}:";
["$ {D /: $ 1: /:}"! = "$ D"] && PATH = "$ {D /: $ 1: /:}";
PATH = "$ {PATH/#: /}";
export PATH = "$ {PATH /%: /}";

Extrayez-le de mon fichier .bashrc . Lorsque vous jouez avec PATH et que vous le perdez, awk/sed/grep devient indisponible :-)

6
GreenFox

Je viens d'utiliser les fonctions de la distribution bash, apparemment présentes depuis 1991. Elles sont toujours dans le paquetage bash-docs sur Fedora et étaient utilisées dans /etc/profile, mais pas plus ...

$ rpm -ql bash-doc |grep pathfunc
/usr/share/doc/bash-4.2.20/examples/functions/pathfuncs
$ cat $(!!)
cat $(rpm -ql bash-doc |grep pathfunc)
#From: "Simon J. Gerraty" <[email protected]>
#Message-Id: <[email protected]>
#Subject: Re: a Shell idea?
#Date: Mon, 09 Oct 1995 21:30:20 +1000


# NAME:
#       add_path.sh - add dir to path
#
# DESCRIPTION:
#       These functions originated in /etc/profile and ksh.kshrc, but
#       are more useful in a separate file.
#
# SEE ALSO:
#       /etc/profile
#
# AUTHOR:
#       Simon J. Gerraty <[email protected]>

#       @(#)Copyright (c) 1991 Simon J. Gerraty
#
#       This file is provided in the hope that it will
#       be of use.  There is absolutely NO WARRANTY.
#       Permission to copy, redistribute or otherwise
#       use this file is hereby granted provided that
#       the above copyright notice and this notice are
#       left intact.

# is $1 missing from $2 (or PATH) ?
no_path() {
        eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}
5
Mr. Wacky

J'ai écrit une réponse à cette ici (en utilisant awk aussi). Mais je ne suis pas sûr que c'est ce que vous cherchez? Au moins, il me semble clair ce que cela fait, au lieu d’essayer de s’intégrer dans une même ligne. Pour un simple revêtement, cependant, qui ne supprime que des choses, je recommande

echo $PATH | tr ':' '\n' | awk '$0 != "/bin"' | paste -sd:

Le remplacement est

echo $PATH | tr ':' '\n' | 
    awk '$0 != "/bin"; $0 == "/bin" { print "/bar" }' | paste -sd:

ou (plus court mais moins lisible)

echo $PATH | tr ':' '\n' | awk '$0 == "/bin" { print "/bar"; next } 1' | paste -sd:

Quoi qu'il en soit, pour la même question et de nombreuses réponses utiles, voir ici .

4

Eh bien, dans bash, puisqu'il prend en charge l'expression régulière, je ferais simplement:

PATH=${PATH/:\/home\/user\/bin/}
3
mat

J'aime les trois fonctions présentées dans la mise à jour de @BenBlank à sa question initiale. Pour les généraliser, j'utilise une forme à 2 arguments qui me permet de définir PATH ou toute autre variable d'environnement que je veux:

path_append ()  { path_remove $1 $2; export $1="${!1}:$2"; }
path_prepend () { path_remove $1 $2; export $1="$2:${!1}"; }
path_remove ()  { export $1="`echo -n ${!1} | awk -v RS=: -v ORS=: '$1 != "'$2'"' | sed 's/:$//'`"; }

Exemples d'utilisation:

path_prepend PATH /usr/local/bin
path_append Perl5LIB "$DEVELOPMENT_HOME/p5/src/perlmods"

Notez que j'ai également ajouté des guillemets pour permettre le traitement correct des noms de chemins contenant des espaces.

1
Cary Millsap

Oui, le fait de placer deux points à la fin de PATH, par exemple, permet de supprimer un chemin un peu moins lourd et sujet aux erreurs.

path_remove ()  { 
   declare i newPATH
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      #echo ${@:${i}:1}
      newPATH="${newPATH//${@:${i}:1}:/}" 
   done
   export PATH="${newPATH%:}" 
   return 0; 
} 

path_remove_all ()  {
   declare i newPATH
   shopt -s extglob
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//+(${@:${i}:1})*([^:]):/}" 
      #newPATH="${newPATH//+(${@:${i}:1})*([^:])+(:)/}" 
   done
   shopt -u extglob 
   export PATH="${newPATH%:}" 
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 
1
cyrill

Si vous souhaitez supprimer les doublons dans $ PATH, la façon la plus élégante, à mon humble avis, serait de ne pas les ajouter en premier lieu. En 1 ligne:

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

Le dossier $ peut être remplacé par n'importe quoi et peut contenir des espaces ("/ home/user/mes documents")

1
MestreLion

La solution pure bash la plus élégante que j'ai trouvée à ce jour:

pathrm () {                                                                      
  local IFS=':'                                                                  
  local newpath                                                                  
  local dir                                                                      
  local pathvar=${2:-PATH}                                                       
  for dir in ${!pathvar} ; do                                                    
    if [ "$dir" != "$1" ] ; then                                                 
      newpath=${newpath:+$newpath:}$dir                                          
    fi                                                                           
  done                                                                           
  export $pathvar="$newpath"                                                        
}

pathprepend () {                                                                 
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="$1${!pathvar:+:${!pathvar}}"                                  
}

pathappend () {                                                                    
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="${!pathvar:+${!pathvar}:}$1"                                  
} 
1
TriangleTodd

La plupart des autres solutions suggérées reposent uniquement sur la correspondance des chaînes et ne tiennent pas compte des segments de chemin contenant des noms spéciaux tels que ., .. ou ~. La fonction bash ci-dessous résout les chaînes de répertoire dans son argument et dans les segments de chemin pour rechercher les correspondances de répertoire logique ainsi que les correspondances de chaîne.

rm_from_path() {
  pattern="${1}"
  dir=''
  [ -d "${pattern}" ] && dir="$(cd ${pattern} && pwd)"  # resolve to absolute path

  new_path=''
  IFS0=${IFS}
  IFS=':'
  for segment in ${PATH}; do
    if [[ ${segment} == ${pattern} ]]; then             # string match
      continue
    Elif [[ -n ${dir} && -d ${segment} ]]; then
      segment="$(cd ${segment} && pwd)"                 # resolve to absolute path
      if [[ ${segment} == ${dir} ]]; then               # logical directory match
        continue
      fi
    fi
    new_path="${new_path}${IFS}${segment}"
  done
  new_path="${new_path/#${IFS}/}"                       # remove leading colon, if any
  IFS=${IFS0}

  export PATH=${new_path}
}

Tester:

$ mkdir -p ~/foo/bar/baz ~/foo/bar/bif ~/foo/boo/bang
$ PATH0=${PATH}
$ PATH=~/foo/bar/baz/.././../boo/././../bar:${PATH}  # add dir with special names
$ rm_from_path ~/foo/boo/../bar/.  # remove same dir with different special names
$ [ ${PATH} == ${PATH0} ] && echo 'PASS' || echo 'FAIL'
1
jwfearn

Quel est le moyen le plus élégant de supprimer un chemin de la variable $ PATH dans Bash?

Quoi de plus élégant que awk?

path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; 

Python! C'est une solution plus lisible et plus facile à gérer, et il est facile d'inspecter pour voir si elle fait vraiment ce que vous voulez.

Vous voulez supprimer le premier élément de chemin?

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"

(Au lieu d'utiliser la variable echo, os.getenv['PATH'] serait un peu plus court et donnerait le même résultat que ci-dessus, mais je crains que Python ne fasse quelque chose avec cette variable d'environnement. Il est donc probablement préférable de la diriger directement de l'environnement se soucier de.)

De même pour supprimer de la fin:

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"

Pour rendre ces fonctions Shell réutilisables que vous pouvez, par exemple, coller dans votre fichier .bashrc:

strip_path_first () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"
}

strip_path_last () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"
}
1
Aaron Hall

Voici un one-liner Perl:

PATH=`Perl -e '$a=shift;$_=$ENV{PATH};s#:$a(:)|^$a:|:$a$#$1#;print' /home/usr/bin`

La variable $a obtient le chemin à supprimer. Les commandes s (substitute) et print agissent implicitement sur la variable $_.

0
J. A. Faucett

Je sais que cette question concerne BASH, ce que tout le monde devrait préférer, mais comme j’aime la symétrie et que j’ai parfois besoin d’utiliser "csh", j’ai construit l’équivalent de "path_prepend ()", "path_append ()" et "path_remove () "solution élégante ci-dessus.

Le Gist est que "csh" n'a pas de fonctions, alors j'ai mis de petits scripts Shell dans mon répertoire bin personnel qui agissent comme des fonctions. Je crée des alias pour SOURCE ces scripts afin de modifier la variable d’environnement désignée.

~/bin/_path_remove.csh:

set _resolve = `eval echo $2`
setenv $1 `eval echo -n \$$1 | awk -v RS=: -v ORS=: '$1 != "'${_resolve}'"' | sed 's/:$//'`;
unset _resolve

~/bin/_path_append.csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_base}:${_resolve}
unset _base _resolve

~/bin/_path_prepend.csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_resolve}:${_base}
unset _base _resolve

~/bin/.cshrc:

…
alias path_remove  "source ~/bin/_path_remove.csh  '\!:1' '\!:2'"
alias path_append  "source ~/bin/_path_append.csh  '\!:1' '\!:2'"
alias path_prepend "source ~/bin/_path_prepend.csh '\!:1' '\!:2'"
…

Vous pouvez les utiliser comme ça ...

%(csh)> path_append MODULEPATH ${HOME}/modulefiles
0

Le ':' suivi est causé par le fait que vous définissez la fin de ligne, pas le séparateur. J'utilise des unités limitées en ressources et j'aime tout emballer dans un seul script, sans ces bizarreries:

path_remove () {
    PATH="$(echo -n $PATH | awk -v RS=: -v ORS= '$0 != "'$1'"{print s _ $0;s=":"}')"
}
0
Coroos

Comme avec @litb, j'ai apporté une réponse à la question " Comment manipuler les éléments $ PATH dans les scripts Shell ", ma réponse principale est donc là.

La fonctionnalité "scindée" de bash et d'autres dérivés de Bourne Shell est parfaitement réalisée avec $IFS, le séparateur inter-champs. Par exemple, pour définir les arguments de position ($1, $2, ...) sur les éléments de PATH, utilisez:

set -- $(IFS=":"; echo "$PATH")

Cela fonctionnera bien tant qu'il n'y aura pas d'espaces dans $ PATH. Le faire fonctionner pour les éléments de chemin contenant des espaces est un exercice non trivial - laissé pour le lecteur intéressé. Il est probablement plus simple de le gérer en utilisant un langage de script tel que Perl.

J'ai aussi un script, clnpath, que j'utilise beaucoup pour configurer mon chemin. Je l'ai documentée dans la réponse à " Comment éviter de dupliquer la variable PATH dans csh ".

0
Jonathan Leffler

Avec la fonction de navigation étendue activée, il est possible d'effectuer les opérations suivantes:

# delete all /opt/local paths in PATH
shopt -s extglob 
printf "%s\n" "${PATH}" | tr ':' '\n' | nl
printf "%s\n" "${PATH//+(\/opt\/local\/)+([^:])?(:)/}" | tr ':' '\n' | nl 

man bash | less -p extglob
0
carlo

J'ai adopté une approche légèrement différente de celle de la plupart des gens ici et me suis concentré sur la manipulation des cordes, comme ceci:

path_remove () {
    if [[ ":$PATH:" == *":$1:"* ]]; then
        local dirs=":$PATH:"
        dirs=${dirs/:$1:/:}
        export PATH="$(__path_clean $dirs)"
    fi
}
__path_clean () {
    local dirs=${1%?}
    echo ${dirs#?}
}

Ce qui précède est un exemple simplifié des fonctions finales que j'utilise. J'ai également créé path_add_before et path_add_after pour vous permettre d'insérer un chemin avant/après un chemin spécifié déjà dans PATH.

L'ensemble complet des fonctions est disponible dans path_helpers.sh dans mes dotfiles . Ils supportent totalement la suppression/l'ajout/la pré-préparation/l'insertion au début/au milieu/à la fin de la chaîne PATH.

0
jimeh

En ajoutant des points dans PATH, nous pourrions aussi faire quelque chose comme: 

path_remove ()  { 
   declare i newPATH
   # put a colon at the beginning & end AND double each colon in-between
   newPATH=":${PATH//:/::}:"   
   for ((i=1; i<=${#@}; i++)); do
       #echo ${@:${i}:1}
       newPATH="${newPATH//:${@:${i}:1}:/}"   # s/:\/fullpath://g
   done
   newPATH="${newPATH//::/:}"
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 


path_remove_all ()  {
   declare i newPATH extglobVar
   extglobVar=0
   # enable extended globbing if necessary
   [[ ! $(shopt -q extglob) ]]  && { shopt -s extglob; extglobVar=1; }
   newPATH=":${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}"     # s/:\/path[^:]*//g
   done
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   # disable extended globbing if it was enabled in this function
   [[ $extglobVar -eq 1 ]] && shopt -u extglob
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 
0
proxxy

Bien que ce soit un très vieux fil, je pensais que cette solution pourrait être intéressante:

PATH="/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
REMOVE="ccache" # whole or part of a path :)
export PATH=$(IFS=':';p=($PATH);unset IFS;p=(${p[@]%%$REMOVE});IFS=':';echo "${p[*]}";unset IFS)
echo $PATH # outputs /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

trouvé sur ce blog post . Je pense que j'aime celui-ci le plus :)

0
mjc

Disons "enlever" les références à/chemin/à/quelque chose à partir de PATH:

Frapper:

PATH=${PATH/something/nope/}

Réponse bonus pour Windows Batch:

set PATH=%PATH:something=nope%

Quelqu'un a une solution plus simple? :)

0
spike83

Ceci est certes élégant, mais utilise sed externe .. .. En outre, il supprime tous les chemins contenant la chaîne de recherche $1. Cela ne laisse pas non plus de traces: à la fin, au cas où le chemin supprimé serait le dernier sur le CHEMIN.

PATH=`echo $PATH | sed 's/:[^:]*$1[^:]*//g'`

Cette alternative laisse en suspens une finale: cependant.

PATH=`echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":"`

PS: Je ne sais pas comment obtenir mes back-ticks dans le code . Donc, les alternatives sont:

PATH=$(echo $PATH | sed 's/:[^:]*$1[^:]*//g')

PATH=$(echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":")
0
Eugene

Dans path_remove_all (par proxxy):

-newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}" 
+newPATH="${newPATH//:${@:${i}:1}*([^:])/}"        # s/:\/path[^:]*//g 
0
marius

Linux from Scratch définit trois fonctions Bash dans /etc/profile:

# Functions to help us manage paths.  Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
        local IFS=':'
        local NEWPATH
        local DIR
        local PATHVARIABLE=${2:-PATH}
        for DIR in ${!PATHVARIABLE} ; do
                if [ "$DIR" != "$1" ] ; then
                  NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
                fi
        done
        export $PATHVARIABLE="$NEWPATH"
}

pathprepend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}

pathappend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}

export -f pathremove pathprepend pathappend

Réf: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html

0
kevinarpe

Ce qui rend ce problème agaçant, ce sont les cas de poteau de clôture parmi les premier et dernier éléments. Le problème peut être résolu de manière élégante en modifiant IFS et en utilisant un tableau, mais je ne sais pas comment réintroduire le colon lorsque le chemin est converti en tableau.

Voici une version légèrement moins élégante qui supprime un répertoire de $PATH en utilisant uniquement la manipulation de chaînes. Je l'ai testé.

#!/bin/bash
#
#   remove_from_path dirname
#
#   removes $1 from user's $PATH

if [ $# -ne 1 ]; then
  echo "Usage: $0 pathname" 1>&2; exit 1;
fi

delendum="$1"
NEWPATH=
xxx="$IFS"
IFS=":"
for i in $PATH ; do
  IFS="$xxx"
  case "$i" in
    "$delendum") ;; # do nothing
    *) [ -z "$NEWPATH" ] && NEWPATH="$i" || NEWPATH="$NEWPATH:$i" ;;
  esac
done

PATH="$NEWPATH"
echo "$PATH"
0
Norman Ramsey

Globing one-liner étendu (bon, en quelque sorte):

path_remove ()  { shopt -s extglob; PATH="${PATH//+(${1})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

Il ne semble pas nécessaire d'échapper aux barres obliques de 1 $.

path_remove ()  { shopt -s extglob; declare escArg="${1//\//\\/}"; PATH="${PATH//+(${escArg})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 
0
carlo

Bonne substance ici . J'utilise celui-ci pour éviter d'ajouter des dupes en premier.

#!/bin/bash
#
######################################################################################
#
# Allows a list of additions to PATH with no dupes
# 
# Patch code below into your $HOME/.bashrc file or where it
# will be seen at login.
#
# Can also be made executable and run as-is.
#
######################################################################################

# add2path=($HOME/bin .)                  ## uncomment space separated list 
if [ $add2path ]; then                    ## skip if list empty or commented out
for nodup in ${add2path[*]}
do
    case $PATH in                 ## case block thanks to MIKE511
    $nodup:* | *:$nodup:* | *:$nodup ) ;;    ## if found, do nothing
    *) PATH=$PATH:$nodup          ## else, add it to end of PATH or
    esac                          ## *) PATH=$nodup:$PATH   prepend to front
done
export PATH
fi
## debug add2path
echo
echo " PATH == $PATH"
echo
0
ongoto

C’est certes élégant, mais il utilise l’externe sed ..__ et supprime tous les chemins contenant la chaîne de recherche $ 1. Cela ne laisse pas non plus de traces: à la fin dans le cas où le chemin supprimé est le dernier sur le chemin.

PATH=`echo $PATH | sed 's/:[^:]*$1[^:]*//g'`

Cette alternative laisse en suspens une finale: cependant.

PATH=`echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":"`

Les alternatives sans backticks sont:

PATH=$(echo $PATH | sed 's/:[^:]*$1[^:]*//g')

PATH=$(echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":")
0
Eugene

Puisque cela a tendance à être assez problématique, comme dans IS NO élégamment, je vous recommande d’éviter le problème en réorganisant la solution: construisez votre PATH plutôt que de le détruire.

Je pourrais être plus précis si je connaissais votre contexte de problème réel. En attendant, je vais utiliser une version du logiciel comme contexte.

Un problème courant avec les versions de logiciels est qu’elles se cassent sur certaines machines, en raison de la façon dont une personne a configuré son shell par défaut (PATH et autres variables d’environnement). La solution élégante consiste à immuniser vos scripts de génération en spécifiant complètement l’environnement Shell. Codez vos scripts de construction pour définir PATH et d’autres variables d’environnement en fonction des éléments d’assemblage que vous contrôlez, tels que l’emplacement du compilateur, des bibliothèques, des outils, des composants, etc. Faites de chaque élément configurable un élément que vous pouvez définir, vérifier et utilisez ensuite de manière appropriée dans votre script.

Par exemple, j'ai une version Java ciblée WebLogic basée sur Maven dont j'ai hérité chez mon nouvel employeur. Le script de construction est réputé fragile, et un autre nouvel employé et moi-même avons passé trois semaines (pas à plein temps, mais ici et là, mais encore de nombreuses heures) à le faire fonctionner sur nos machines. Une étape essentielle a été de prendre le contrôle de PATH afin de savoir exactement quel Java, quel Maven et quel WebLogic était appelé. J'ai créé des variables d'environnement pour pointer vers chacun de ces outils, puis j'ai calculé le PATH en fonction de ceux-ci et de quelques autres. Des techniques similaires ont apprivoisé les autres paramètres configurables, jusqu'à ce que nous ayons finalement créé une construction reproductible.

En passant, n'utilisez pas Maven, Java convient, et n'achetez WebLogic que si vous avez absolument besoin de la mise en cluster (sinon, non, et surtout pas de ses fonctionnalités propriétaires).

Meilleurs vœux.

0
Rob Williams