Qual è il modo più elegante per rimuovere un percorso dalla variabile $ PATH in Bash?


114

O più in generale, come rimuovo un elemento da un elenco separato da due punti in una variabile d'ambiente Bash?

Pensavo di aver visto un modo semplice per farlo anni fa, utilizzando le forme più avanzate di espansione delle variabili Bash, ma se è così ne ho perso traccia. Una rapida ricerca su Google ha portato sorprendentemente pochi risultati rilevanti e nessuno che io definirei "semplice" o "elegante". Ad esempio, due metodi che utilizzano rispettivamente sed e awk:

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)

Non esiste niente di semplice? C'è qualcosa di analogo a una funzione split () in Bash?

Aggiornamento:
sembra che debba scusarmi per la mia domanda volutamente vaga; Ero meno interessato a risolvere un caso d'uso specifico che a provocare una buona discussione. Fortunatamente, ho capito!

Ci sono alcune tecniche molto intelligenti qui. Alla fine, ho aggiunto le seguenti tre funzioni alla mia cassetta degli attrezzi. La magia avviene in path_remove, che si basa in gran parte sull'uso intelligente della awkvariabile RS di Martin York .

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/:$//'`; }

L'unico vero problema è l'uso di sedper rimuovere i due punti finali. Considerando quanto sia semplice il resto della soluzione di Martin, però, sono abbastanza disposto a conviverci!


Domanda correlata: Come si manipolano gli elementi $ PATH negli script della shell?


Per qualsiasi variabile: WORK=`echo -n ${1} | awk -v RS=: -v ORS=: '$0 != "'${3}'"' | sed 's/:$//'`; eval "export ${2}=${WORK}"ma devi chiamarla come func $VAR VAR pattern(basato su @ martin-york e @ andrew-aylett)
vesperto

Risposte:


51

Un minuto con awk:

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

Modifica: risponde ai commenti di seguito:

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

Modifica in risposta al problema di sicurezza: (non pertinente alla domanda)

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

Ciò rimuove tutti i due punti finali rimasti eliminando le ultime voci, che si aggiungerebbero effettivamente .al tuo percorso.


1
Non riesce quando si tenta di rimuovere l'ultimo elemento o più elementi: nel primo caso aggiunge la directory corrente (come stringa vuota; un potenziale buco di sicurezza), nel secondo caso aggiunge `` come elemento del percorso.
Fred Foo

1
@larsmans: funziona bene per me. Nota: Empty non è la stessa directory corrente che è "./"
Martin York

2
Una stringa vuota come "membro" della PATHvariabile indica , come regola speciale, la directory corrente in tutte le shell Unix a partire almeno dalla V7 Unix del 1979. Lo fa ancora in bash. Controlla il manuale o prova tu stesso.
Fred Foo

1
@Martin: POSIX non richiede questo comportamento, ma lo documenta e lo consente: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Fred Foo

1
C'è un problema ancora più sottile quando si rimuove l'ultimo elemento con questo: awk sembra aggiungere un byte nullo alla fine della stringa . Significa che se aggiungi un'altra directory a PATH in un secondo momento, di fatto non verrà cercata.
sschuberth

55

Il mio sporco trucco:

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

18
Non è mai un buon segno quando la soluzione più ovvia è quella di de -Automatizzazione il processo. :-D
Ben Blank

Molto più sicuro delle alternative "eleganti".
Jortstek

44

Dato che il grosso problema con la sostituzione sono i casi finali, che ne dici di rendere i casi finali non diversi dagli altri casi? Se il percorso aveva già due punti all'inizio e alla fine, potremmo semplicemente cercare la nostra stringa desiderata racchiusa tra due punti. Così com'è, possiamo facilmente aggiungere quei due punti e rimuoverli in seguito.

# 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 :).


2
Aggiungerei questa sezione tutorial per un po 'di glassa extra: tldp.org/LDP/abs/html/string-manipulation.html
Cyber ​​Oliveira

1
L'ho usato perché sembrava la soluzione più semplice. È stato super veloce e facile, e puoi facilmente controllare il tuo lavoro con echo $ WORK subito prima dell'ultima riga in cui effettivamente cambi la variabile PATH.
Phil Gran

2
Assolutamente un piccolo gioiello. Esattamente quello che stavo cercando di fare quando ho trovato questo post. -Grazie Andrew! BTW: Forse vorresti aggiungere virgolette attorno a ": $ PATH:", nel caso in cui dovesse contenere spazi (lo stesso su "/ usr / bin") e l'ultima riga "$ WORK".

2
Grazie, @ PacMan-- :). Sono abbastanza sicuro (l'ho appena provato) non hai bisogno di spazi per gli assegnamenti WORKe PATHpoiché l'espansione delle variabili avviene dopo che la riga è stata analizzata in sezioni per l'assegnazione delle variabili e l'esecuzione dei comandi. REMOVEpotrebbe dover essere citato, oppure potresti semplicemente mettere la tua stringa direttamente nella sostituzione se è una costante.
Andrew Aylett

Mi sono sempre confuso quando le stringhe sono state conservate, quindi ho iniziato sempre a virgolette doppie. Grazie ancora per aver chiarito questo. :)

26

Ecco la soluzione più semplice che posso escogitare:

#!/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'esempio precedente rimuoverà qualsiasi elemento in $ PATH che contiene "usr". Puoi sostituire "* usr *" con "/ home / user / bin" per rimuovere solo quell'elemento.

aggiornamento per sschuberth

Anche se penso che gli spazi in a $PATHsiano un'idea orribile , ecco una soluzione che la gestisce:

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[*]}");

o

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

2
Come una riga: PATH = $ (IFS = ':'; t = ($ PATH); unset IFS; t = ($ {t [@] %% * usr *}); IFS = ':'; echo "$ {t [*]} ");
nicerobot

1
Questa soluzione non funziona con i percorsi in PATH che contengono spazi; li sostituisce con due punti.
sschuberth

11

Ecco una frase che, nonostante le risposte attualmente accettate e più votate , non aggiunge caratteri invisibili a PATH e può far fronte a percorsi che contengono spazi:

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

Personalmente, trovo anche questo facile da leggere / capire e coinvolge solo comandi comuni invece di usare awk.


2
... e se vuoi qualcosa che possa far fronte anche alle nuove righe nei nomi dei file, potresti usare questo: export PATH=$(p=$(echo $PATH | tr ":" "\0" | grep -v -z "/cygwin/" | tr "\0" ":"); echo ${p%:}) (anche se probabilmente, potresti chiederti perché ne hai bisogno, se lo fai :))
ehdr

Questo rimuoverà le corrispondenze parziali, che probabilmente non è quello che vuoi; Vorrei usare grep -v "^/path/to/remove\$"orgrep -v -x "/path/to/remove"
ShadSterling

Ottima soluzione, ma pensi davvero che trsia più comune di awk? ;)
K.-Michael Aye

1
Assolutamente. Gli ambienti leggeri, come Git Bash su Windows, sono piuttosto dotati di uno strumento semplice come trpiuttosto che di un interprete awk.
sschuberth

1
@ehdr: è necessario sostituire echo "..."con printf "%s" "..."affinché funzioni su percorsi simili -ee simili. Vedi stackoverflow.com/a/49418406/102441
Eric

8

Ecco una soluzione che:

  • è puro Bash,
  • non richiama altri processi (come 'sed' o 'awk'),
  • non cambia IFS,
  • non esegue il fork di un sub-shell,
  • gestisce i percorsi con spazi e
  • rimuove tutte le occorrenze dell'argomento in PATH.

    removeFromPath () {
       pd locale
       p = ": $ 1:"
       d = ": $ PATH:"
       d = $ {d // $ p /:}
       d = $ {d / #: /}
       PATH = $ {d /%: /}
    }

4
Mi piace questa soluzione. Forse rendere i nomi delle variabili più descrittivi?
Anukool

molto bella. Tuttavia, non funziona per i segmenti di percorso contenenti un asterisco (*). A volte ci arrivano accidentalmente.
Jörg

6

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

Estrarlo dal mio file .bashrc. Quando giochi con PATH e si perde, awk / sed / grep non è più disponibile :-)


1
Questo è un ottimo punto. (Non mi è mai piaciuto eseguire utilità esterne per cose semplici come questa).

6

La migliore opzione di bash pura che ho trovato finora è la seguente:

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
}

Questo si basa sulla risposta non del tutto corretta ad Aggiungi directory a $ PATH se non è già presente su Superuser.


Anche questo è abbastanza buono. L'ho provato. Se in PATH è presente un percorso duplicato (ad es. Due esattamente uguali), solo uno di essi viene rimosso. Puoi anche trasformarlo in un one-liner:removePath () { PATH=${PATH/":$1"/}; PATH=${PATH/"$1:"/}; }

Questa soluzione non riesce quando $PATHcontiene una sottocartella del percorso di destinazione (ovvero da eliminare). Ad esempio: a:abc/def/bin:b-> a/bin:b, quando abc/defdeve essere cancellato.
Robin Hsu

5

Ho appena usato le funzioni nella distribuzione bash, che apparentemente sono presenti dal 1991. Queste sono ancora nel pacchetto bash-docs su Fedora, e venivano usate in /etc/profile, ma non più ...

$ 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" <sjg@zen.void.oz.au>
#Message-Id: <199510091130.VAA01188@zen.void.oz.au>
#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 <sjg@zen.void.oz.au>

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

4

Ho scritto una risposta qui (usando anche awk). Ma non sono sicuro che sia quello che stai cercando? Almeno mi sembra chiaro cosa fa, invece di cercare di rientrare in una riga. Per un semplice rivestimento, però, che rimuove solo cose, lo consiglio

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

La sostituzione è

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

o (più breve ma meno leggibile)

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

Comunque, per la stessa domanda e molte utili risposte, vedi qui .


E se vuoi rimuovere una riga che contiene una stringa parziale usa awk '$0 !~ "/bin"'. Vale a dire mantenere le righe che non contengono "/ bin" con l'operatore awk !~.
thoni56

3

Bene, in bash, poiché supporta l'espressione regolare, farei semplicemente:

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

Non è solo l'espansione del percorso, non le espressioni regolari?
dreamlax

2
Sebbene bash supporti le espressioni regolari (a partire da bash 3), questo non è un esempio, questa è una sostituzione di variabile.
Robert Gamble

4
Questa è l'espansione della variabile del pattern e la soluzione presenta diversi problemi. 1) non corrisponderà al primo elemento. 2) corrisponderà a tutto ciò che inizia con "/ home / user / bin", non solo "/ home / user / bin". 3) richiede caratteri speciali di escape. Nella migliore delle ipotesi, direi che questo è un esempio incompleto.
nicerobot

2

Mi piacciono le tre funzioni mostrate nell'aggiornamento di @ BenBlank alla sua domanda originale. Per generalizzarli, utilizzo un modulo a 2 argomenti, che mi consente di impostare PATH o qualsiasi altra variabile d'ambiente che voglio:

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/:$//'`"; }

Esempi di utilizzo:

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

Si noti che ho anche aggiunto alcune virgolette per consentire la corretta elaborazione dei nomi di percorso che contengono spazi.


2

Qual è il modo più elegante per rimuovere un percorso dalla variabile $ PATH in Bash?

Cosa c'è di più elegante di awk?

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

Pitone! È una soluzione più leggibile e gestibile ed è facile da ispezionare per vedere che sta davvero facendo quello che vuoi.

Dici di voler rimuovere il primo elemento del percorso?

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

(Invece di tubare da echo, os.getenv['PATH']sarebbe un po 'più corto e fornirebbe lo stesso risultato di quanto sopra, ma sono preoccupato che Python possa fare qualcosa con quella variabile d'ambiente, quindi probabilmente è meglio canalizzarlo direttamente dall'ambiente a cui tieni .)

Allo stesso modo per rimuovere dalla fine:

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

Per rendere queste funzioni di shell riutilizzabili che puoi, ad esempio, inserire nel tuo file .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

Sì, mettere i due punti alla fine di PATH, ad esempio, rende la rimozione di un percorso un po 'meno goffa e soggetta a errori.

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

Se sei preoccupato per la rimozione dei duplicati in $ PATH, il modo più elegante, IMHO, sarebbe non aggiungerli in primo luogo. In 1 riga:

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

$ cartella può essere sostituita da qualsiasi cosa e può contenere spazi ("/ home / utente / i miei documenti")


1

La soluzione bash pura più elegante che ho trovato fino ad oggi:

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

La maggior parte delle altre soluzioni proposte si basano solo su string matching e non tengono conto di segmenti di percorso che contengono i nomi speciali come ., ..o~ . La funzione bash di seguito risolve le stringhe di directory nel suo argomento e nei segmenti di percorso per trovare corrispondenze di directory logiche e corrispondenze di stringhe.

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

Test:

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

più uno per fuori area
Martin York

1

Linux from Scratch definisce tre funzioni Bash in /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

Rif: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html


1

So che questa domanda chiede di BASH, che tutti dovrebbero preferire, ma poiché mi piace la simmetria e talvolta mi viene richiesto di usare "csh", ho creato l'equivalente di "path_prepend ()", "path_append ()" e "path_remove () "elegante soluzione sopra.

Il succo è che "csh" non ha funzioni, quindi metto piccoli script di shell nella mia directory bin personale che agiscono come le funzioni. Creo alias per SOURCE quegli script per apportare modifiche alle variabili di ambiente designate.

~ / 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'"

Puoi usarli in questo modo ...

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

0

Poiché questo tende ad essere piuttosto problematico, poiché NON esiste un modo elegante, consiglio di evitare il problema riorganizzando la soluzione: costruisci il tuo PERCORSO piuttosto che tentare di eliminarlo.

Potrei essere più specifico se conoscessi il contesto del tuo vero problema. Nel frattempo, userò una build software come contesto.

Un problema comune con le build del software è che si interrompe su alcune macchine, in ultima analisi a causa di come qualcuno ha configurato la propria shell predefinita (PATH e altre variabili di ambiente). L'elegante soluzione è rendere immuni gli script di compilazione specificando completamente l'ambiente della shell. Codifica i tuoi script di compilazione per impostare il PERCORSO e altre variabili di ambiente in base all'assemblaggio di pezzi che controlli, come la posizione del compilatore, librerie, strumenti, componenti, ecc. Rendi ogni elemento configurabile qualcosa che puoi impostare, verificare e quindi usalo in modo appropriato nel tuo script.

Ad esempio, ho una build Java mirata a WebLogic basata su Maven che ho ereditato dal mio nuovo datore di lavoro. Lo script di build è noto per essere fragile, e un altro nuovo dipendente e io abbiamo trascorso tre settimane (non a tempo pieno, solo qua e là, ma ancora molte ore) per farlo funzionare sulle nostre macchine. Un passaggio essenziale è stato che ho preso il controllo del PATH in modo da sapere esattamente quale Java, quale Maven e quale WebLogic veniva invocato. Ho creato variabili di ambiente per puntare a ciascuno di questi strumenti, quindi ho calcolato il PERCORSO in base a quelli più alcuni altri. Tecniche simili hanno domato le altre impostazioni configurabili, fino a quando non abbiamo finalmente creato una build riproducibile.

A proposito, non usare Maven, Java va bene e acquista WebLogic solo se hai assolutamente bisogno del suo clustering (ma per il resto no, e soprattutto non delle sue funzionalità proprietarie).

Auguri.


a volte non hai accesso root e il tuo amministratore gestisce il tuo PATH. Certo, potresti crearne uno tuo, ma ogni volta che il tuo amministratore sposta qualcosa devi capire dove l'ha messo. Questo tipo di sconfigge lo scopo di avere un amministratore.
Shep

0

Come con @litb, ho fornito una risposta alla domanda " Come si manipolano gli elementi $ PATH negli script di shell ", quindi la mia risposta principale è lì.

La funzionalità 'split' in bashe altri derivati ​​della shell Bourne si ottiene in modo più preciso con $IFSil separatore tra campi. Ad esempio, per impostare gli argomenti posizionali ( $1, $2, ...) agli elementi di PATH, uso:

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

Funzionerà bene finché non ci sono spazi in $ PATH. Farlo funzionare per elementi di percorso contenenti spazi è un esercizio non banale, lasciato al lettore interessato. Probabilmente è più semplice gestirlo utilizzando un linguaggio di scripting come Perl.

Ho anche uno script, clnpathche uso ampiamente per impostare il mio PERCORSO. L'ho documentato nella risposta a " Come evitare di duplicare la variabile PATH in csh ".


IFS =: a = ($ PATH); IFS = anche la divisione è piacevole. funziona se contengono anche spazi. ma poi hai un array e devi giocherellare con i cicli for e simili per rimuovere i nomi.
Johannes Schaub - litb

Sì; diventa complicato - come con il mio commento aggiornato, è probabilmente più semplice usare un linguaggio di scripting a questo punto.
Jonathan Leffler

0

Ciò che rende fastidioso questo problema sono i casi di fencepost tra il primo e l'ultimo elemento. Il problema può essere risolto elegantemente cambiando IFS e utilizzando un array, ma non so come reintrodurre i due punti una volta che il percorso è stato convertito in formato array.

Ecco una versione leggermente meno elegante che rimuove una directory $PATHdall'utilizzo della sola manipolazione delle stringhe. L'ho provato.

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

Ecco un monile di Perl:

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

La $avariabile ottiene il percorso da rimuovere. I comandi s(sostituto) e printagiscono implicitamente sulla $_variabile.


0

Roba buona qui. Lo uso per evitare di aggiungere doppioni in primo luogo.

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

1
Puoi semplificare la tua dichiarazione del caso aggiungendo i due punti case ":$PATH:" in (*:"$nodup":*) ;; (*) PATH="$PATH:$nodup" ;; esac
iniziali

0

Con il globbing esteso abilitato è possibile eseguire le seguenti operazioni:

# 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

One-liner globbing esteso (beh, più o meno):

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

Non sembra esserci bisogno di sfuggire alle barre in $ 1.

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

0

Aggiungendo i due punti a PATH potremmo anche fare qualcosa come:

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

In path_remove_all (tramite proxy):

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

0

Sebbene questo sia un thread molto vecchio, ho pensato che questa soluzione potesse essere interessante:

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

l'ho trovato in questo post del blog . Penso che questo mi piaccia di più :)


0

Ho adottato un approccio leggermente diverso rispetto alla maggior parte delle persone qui e mi sono concentrato specificamente sulla manipolazione delle stringhe, in questo modo:

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#?}
}

Quanto sopra è un esempio semplificato delle funzioni finali che utilizzo. Ho anche creato path_add_beforee path_add_afterconsentendo di inserire un percorso prima / dopo un percorso specificato già in PATH.

Il set completo di funzioni è disponibile in path_helpers.sh nei miei dotfile . Supportano completamente la rimozione / aggiunta / preposizione / inserimento all'inizio / al centro / alla fine della stringa PATH.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.