Remover entradas duplicadas da variável PATH

9

Modifico meu arquivo .bashrc frequentemente e depois o fonte. No entanto, quando tenho coisas como export PATH="~/bin:~/perl5/bin:$PATH"no meu arquivo, a PATHvariável de ambiente cresce toda vez que eu origino o arquivo.

Por exemplo, na primeira vez que o .bashrc é originado, a PATHvariável consiste em ~/bin:~/perl5/bin:/usr/bin:/bin.

A segunda vez que consiste ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

A terceira vez que consiste ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Existe uma maneira simples de fazê-lo adicionar apenas algo que ainda não esteja no PATH?

Christopher Bottoms
fonte

Respostas:

12

Use a função pathmunge () disponível na maioria das distros /etc/profile:

pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
   if [ "$2" = "after" ] ; then
      PATH=$PATH:$1
   else
      PATH=$1:$PATH
   fi
fi
}

editar : para zshusuários, typeset -U <variable_name>deduplicará as entradas do caminho.

Sam Halicke
fonte
Obrigado! Não está /etc/profileno Debian Lenny, então eu o incluo no meu .bashrc.
Christopher Bottoms
Uso: pathmunge /some/pathvai colocar /some/pathno início $PATHe pathmunge /some/path aftervai colocar /some/pathno final$PATH
Christopher Bottoms
Cleaner maneira de fazer a verificação para ver se o diretório hew existe no caminho atual, em conchas festança modernos: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
22610 Christopher Cashell
Eu não usaria isso. Se minha versão ingênua dizia PATH = / foo: $ PATH, isso significa que eu quero que / foo vença. pathmunge /foo beforenão consegue isso. Uma maneira melhor seria adicionar / foo no início e remover as duplicatas posteriormente.
Don escotilha
3

Eu estava tendo esse problema, então usei uma combinação de técnicas listadas na pergunta StackOverflow . A seguir, o que eu usei para desduplicar a variável PATH real que já havia sido definida, pois não queria modificar o script base.

    tmppath=(${PATH// /@})
    array=(${tmppath//:/ })
    for i in "${array[@]//@/ }"
    do
        if ! [[ $PATH_NEW =~ "$i" ]]; then
            PATH_NEW="${PATH_NEW}$i:";
        fi
    done;
    PATH="${PATH_NEW%:}"
    export PATH
    unset PATH_NEW

Você sempre pode otimizar isso um pouco mais, mas eu tinha um código extra no original para exibir o que estava acontecendo para garantir que ele estivesse definindo corretamente as variáveis. A outra coisa a notar é que eu realizo o seguinte

  1. substitua qualquer caractere ESPAÇO por um caractere @
  2. dividir a matriz
  3. loop através da matriz
  4. substitua quaisquer caracteres @ na cadeia de caracteres do elemento por um espaço

Isso é para garantir que eu possa lidar com diretórios com espaços (os diretórios pessoais do Samba com nomes de usuário do Active Directory podem ter espaços!)

netniV
fonte
AVISO: Esta implementação possui um erro grave !! será removido /binse houver outras entradas, como /usr/binporque a regexp corresponde mesmo que as duas entradas não sejam as mesmas!
Sukima 23/04/19
Aqui é uma implementação alternativa que correções de bug: github.com/sukima/dotfiles/blob/...
Sukima
1

Defina seu caminho explicitamente.

Cakemox
fonte
Obrigado. Tentei definir o caminho explicitamente, mas tenho um arquivo .bashrc que uso em vários ambientes, portanto o padrão exato PATHnem sempre é o mesmo.
19610 Christopher Bottoms
1

Apenas uma sequência:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
fonte
Obrigado. Uma dificuldade que isso me causaria é que ele reorganiza alfabeticamente (ou ASCIIbeticamente) o conteúdo de $PATH. Gosto de colocar diretórios específicos no início de $PATH(curtir /home/username) para que minhas cópias pessoais de executáveis ​​sejam executadas em vez dos padrões internos.
19610 Christopher Bottoms
1

Posso pensar em duas maneiras diferentes de resolver isso. O primeiro é iniciar o .bashrc com uma linha que defina explicitamente o PATH base, dessa forma, toda vez que você o origina, ele é redefinido para a base antes de adicionar diretórios adicionais.

Por exemplo, adicione:

# Reset the PATH to prevent duplication and to make sure that we include
# everything we want.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Como alternativa, você pode procurar um item antes de adicioná-lo ao caminho. Para fazer isso, você usaria algo como:

if ! [[ $PATH =~ '~/perl5/bin' ]]
then
    PATH="~/perl5/bin:$PATH"
fi

O último tende a ficar um pouco repetitivo se você adicionar muitas entradas, no entanto, por isso, eu costumo ficar com o primeiro. Se você quisesse usar isso e planejasse adicionar muitas entradas, seria sensato escrever uma função bash para lidar com isso.

Nota: A segunda opção pode funcionar apenas como está escrito nas versões modernas do bash. O suporte à expressão regular não é um recurso do Bourne Shell (/ bin / sh) e pode não existir em outros shells. Além disso, o uso de aspas pode não ser necessário ou pode até causar problemas em algumas versões mais recentes do bash.

Christopher Cashell
fonte
Obrigado. Tentei definir o caminho explicitamente, mas tenho um arquivo .bashrc que uso em vários ambientes, portanto o padrão exato PATHnem sempre é o mesmo.
Christopher Bottoms
Você ainda pode lidar com isso, verificando o script para ver qual é o nome do host local e, em seguida, definindo o caminho absoluto de forma adequada para esse servidor.
precisa
Apenas um erro de digitação: o! está faltando no if. Além disso, é surpreendentemente (para mim) fácil transformar isso em uma função que circula argumentos separados por espaço, para que você possa apenas dizer: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (acho que a caseinstrução usada é mais portátil) , mas você ifé mais claro para mim). EDITAR coincidência estranha que essa pergunta também adiciona perl5!
13ren
@ 13ren: Obrigado pela nota. Também acabei de perceber que usava aspas simples na linha de caminho PATH, em vez de aspas duplas, como deveria. Conforme escrito, ele teria substituído o caminho existente pelo nome da variável. Opa! Aparentemente, isso foi uma entrada ruim para mim para erros de digitação; ambos estão consertados agora.
precisa
0

Aqui está a minha solução: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Um liner fácil e agradável que não deixa um rastro:

AJ.
fonte