Uma maneira mais confortável de editar um longo $ PATH?

35

Quero adicionar, em ~ / .bashrc, alguns diretórios ao meu $ PATH.

Meu $ PATH é bastante longo, portanto é um pouco difícil ver quais diretórios ele contém e em que ordem.

Eu sei que posso modificar meu ~ / .bashrc para:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

facilitaria a leitura. Mas eu queria saber se, durante os últimos anos, o Bash adquiriu alguma sintaxe que facilita a especificação de um longo caminho. Por exemplo, estou fantasiando sobre uma sintaxe semelhante a:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

Eu sei que essa sintaxe é inválida. Fiquei me perguntando se há algo tão fácil. Existe?

Niccolo M.
fonte
O tutorial tradicional para definir o caminho via PATH=foo:$PATHparece errado porque manter o crescimento cada vez source ~/.bashrce até mesmo exec bashnão pode ajudar, uma vez que o $PATHé export.
林果 皞

Respostas:

25

Eu uso um conjunto de funções de conveniência para acrescentar ou acrescentar um caminho a uma variável. As funções vêm no pacote de distribuição do Bash em um arquivo de contribuição chamado "pathfuncs".

  • add_path adicionará a entrada ao final da variável PATH
  • pre_path adicionará a entrada ao início da variável PATH
  • del_path removerá a entrada da variável PATH, onde quer que esteja

Se você especificar uma variável como o segundo argumento, ela será usada em vez de PATH.

Por conveniência, aqui estão eles:

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

Se você os adicionar ao seu arquivo de inicialização do bash, poderá adicioná-lo ao PATH assim:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

Ou especifique uma variável diferente:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

Eu uso esse método nos meus arquivos rc colocando os pre_paths primeiro e os add_paths segundo. Isso facilita a compreensão de todas as alterações no meu caminho. Outro benefício é que as linhas são curtas o suficiente para que eu possa adicionar um comentário à linha, se necessário.

E como essas são funções, você pode usá-las interativamente a partir da linha de comando, como dizendo add_path $(pwd)para adicionar o diretório atual ao caminho.

Estrela do Mar
fonte
Obrigado. Eu verifiquei seu código e ele funciona. Surpreendentemente, eu também encontrei um uso para del_path (A "." Entra em meu PATH em algumas situações, o diabo sabe de onde, então eu fiz del_path .).
Nicolau M.
Oi. é possível originar (incluir) esses caminhos do script bashrc ou devo copiá-los / colá-los para lá?
Cristiano
@Cristiano Ou vai funcionar. É realmente com você.
Starfish
11

OK, descobri a seguinte solução, que acho elegante (no que diz respeito à sintaxe do shell). Ele usa a sintaxe de matriz do Bash e também uma maneira elegante de juntar elementos:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

ALERTA!

Acontece que esta solução tem um problema : Ao contrário das soluções @terdon e @Starfish, ela não verifica primeiro se os caminhos já estão no PATH. Então, como eu quero colocar esse código em ~ / .bashrc (e não em ~ / .profile), caminhos duplicados aparecerão no PATH. Portanto, não use esta solução (a menos que você a coloque em ~ / .profile (ou, melhor, ~ / .bash_profile, pois possui sintaxe específica do Bash)).

Niccolo M.
fonte
11
Muito agradável. Você pode aceitar uma resposta para que outras pessoas não venham aqui para oferecer uma solução quando você já a encontrou? Obrigado
Básico
Caminhos duplicados não são realmente um problema. É altamente improvável que você tenha adicionado diretórios suficientes PATHpara realmente causar problemas de desempenho (especialmente porque o shell armazena em cache as pesquisas bem-sucedidas).
chepner
5

Eu uso a função abaixo no meu ~/.bashrc. É algo que recebi de um administrador de sistemas no meu antigo laboratório, mas acho que ele não escreveu. Basta adicionar essas linhas ao seu ~/.profileou ~/.bashrc:

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

Isso tem várias vantagens:

  • Adicionando novos diretórios para o $PATHtrivial: pathmunge /foo/bar;
  • Evita entradas duplicadas;
  • Você pode optar por adicionar uma nova entrada ao início ( pathmunge /foo/barou ao final ( pathmunge /foo/bardepois) do) $PATH.

O arquivo de inicialização do seu shell conteria algo como:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end
Terdon
fonte
Obrigado. Mas vou escolher a solução da @ Starfish porque a dele não aparece grep.
Nicolau M.
2
@NiccoloM. não tem problema, aceite qual você preferir. Porém, tenha cuidado com a abordagem da estrela do mar, pois ela executará código arbitrário através do evalcomando para que você possa causar algum dano sério se o executar com o argumento errado.
terdon
Observe que existe uma função mais rápida no redhat para fazer isso sem comando externo grep, consulte bugzilla.redhat.com/show_bug.cgi?id=544652#c7
4

Quero adicionar, em ~ / .bashrc, alguns diretórios ao meu $ PATH.

Eu uso o seguinte no Cygwin. Deve funcionar em outras versões do bash. Você pode remover a unset PATHversão atual PATH(se você fizer isso, talvez precise descobrir como adicionar os :separadores corretamente).

Nota:

  • Uma vez eu tive essa funcionalidade em uma bashfunção, mas a perdi após uma falha no disco.

No meu .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

Em ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows
DavidPostill
fonte
Obrigado. Sua resposta me inspirou a trabalhar em uma solução semelhante. (Para o meu gosto armazenar os caminhos num ficheiro separado é incomodativo.)
Nicolau M.
1

Eu uso isso no meu .bashrc (e também no meu .zshrc, pois normalmente uso o zsh quando disponível, em vez do bash). É verdade que exige que eu adicione diretórios manualmente, mas uma vantagem é que, à medida que o atualizo, posso copiá-lo para novos servidores e não me preocupar com o PATH em um novo servidor criado com diretórios que não existem lá.

##
## CAMINHO
##
## Em vez de apenas atrapalhar nosso PATH com diretórios que podem
## não for apropriado para este servidor, tente ser inteligente sobre o que adicionamos
##
PATH = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin
[-d / cs / sbin] && PATH = / cs / sbin: $ PATH
[-d / cs / bin] && PATH = / cs / bin: $ PATH
[-d / usr / ucb] && PATH = $ PATH: / usr / ucb
[-d / usr / ccs / bin] && PATH = $ PATH: / usr / ccs / bin
[-d / usr / local / ssl / bin] && PATH = $ PATH: / usr / local / ssl / bin
[-d / usr / krb5 / bin] && PATH = $ PATH: / usr / krb5 / bin
[-d / usr / krb5 / sbin] && PATH = $ PATH: / usr / krb5 / sbin
[-d / usr / kerberos / sbin] && PATH = $ PATH: / usr / kerberos / sbin
[-d / usr / kerberos / bin] && PATH = $ PATH: / usr / kerberos / bin
[-d /cs/local/jdk1.5.0/bin] && PATH = $ PATH: /cs/local/jdk1.5.0/bin
[-d /usr/java/jre1.5.0_02/bin] && PATH = $ PATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/bin] && PATH = $ PATH: /usr/java1.2/bin
[-d /cs/local/perl5.8.0/bin] && PATH = $ PATH: /cs/local/perl5.8.0/bin
[-d / usr / perl5 / bin] && PATH = $ PATH: / usr / perl5 / bin
[-d / usr / X11R6 / bin] && PATH = $ PATH: / usr / X11R6 / bin
[-d / etc / X11] && PATH = $ PATH: / etc / X11
[-d / opt / sfw / bin] && PATH = $ PATH: / opt / sfw / bin
[-d / usr / local / apache / bin] && PATH = $ PATH: / usr / local / apache / bin
[-d / usr / apache / bin] && PATH = $ PATH: / usr / apache / bin
[-d / cs / admin / bin] && PATH = $ PATH: / cs / admin / bin
[-d / usr / openwin / bin] && PATH = $ PATH: / usr / openwin / bin
[-d / usr / xpg4 / bin] && PATH = $ PATH: / usr / xpg4 / bin
[-d / usr / dt / bin] && PATH = $ PATH: / usr / dt / bin

Eu faço o mesmo pelo meu MANPATH:

##
## MANPATH
##
## Em vez de apenas encher nosso MANPATH com diretórios que podem
## não for apropriado para este servidor, tente ser inteligente sobre o que adicionamos
##
MANPATH = / usr / local / man
[-d / usr / share / man] && MANPATH = $ MANPATH: / usr / share / man
[-d / usr / local / compartilhamento / homem] && MANPATH = $ MANPATH: / usr / local / compartilhamento / homem
[-d / usr / man] && MANPATH = $ MANPATH: / usr / man
[-d / cs / man] && MANPATH = $ MANPATH: / cs / man
[-d / usr / krb5 / man] && MANPATH = $ MANPATH: / usr / krb5 / man
[-d / usr / kerberos / man] && MANPATH = $ MANPATH: / usr / kerberos / man
[-d / usr / local / ssl / man] && MANPATH = $ MANPATH: / usr / local / ssl / man
[-d /cs/local/jdk1.5.0/man] && MANPATH = $ MANPATH: /cs/local/jdk1.5.0/man
[-d /usr/java/jre1.5.0_02/man] && MANPATH = $ MANPATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/man] && MANPATH = $ MANPATH: /usr/java1.2/man
[-d / usr / X11R6 / man] && MANPATH = $ MANPATH: / usr / X11R6 / man
[-d / usr / local / apache / man] && MANPATH = $ MANPATH: / usr / local / apache / man
[-d / usr / local / mysql / man] && MANPATH = $ MANPATH: / usr / local / mysql / man
[-d /cs/local/perl5.8.0/man] && MANPATH = $ MANPATH: /cs/local/perl5.8.0/man
[-d / usr / perl5 / man] && MANPATH = $ MANPATH: / usr / perl5 / man
[-d / usr / local / perl / man] && MANPATH = $ MANPATH: / usr / local / perl / man
[-d /usr/local/perl5.8.0/man] && MANPATH = $ MANPATH: /usr/local/perl5.8.0/man
[-d / usr / openwin / man] && MANPATH = $ MANPATH: / usr / openwin / man

Além de ter um único arquivo que eu posso copiar para sistemas em ambientes diferentes, sem medo de adicionar diretórios inexistentes ao PATH, essa abordagem também tem a vantagem de permitir que eu especifique a ordem em que os diretórios devem aparecer no PATH. Como a primeira linha de cada definição redefine completamente a variável PATH, eu posso atualizar meu .bashrc e originar após a edição para atualizar meu shell sem adicionar entradas duplicadas (que eu costumava experimentar há muito tempo quando simplesmente comecei com " $ PATH = $ PATH: / new / dir ". Isso garante que eu receba uma cópia limpa na ordem que desejar.

Brian Snook
fonte
11
sugerindo uma alternativa: d="/usr/share/man" ; [ -d "$d" ] && MANPATH="$MANPATH:${d}"será mais curto e mais fácil adicionar um novo diretório (basta copiar uma linha e editar a primeira parte "d = ...."). No entanto, para o PATH, acho que você terminará com muitos dirs no PATH, o que nem sempre é bom (e se algum comando "foo" existir em um dos caminhos menos conhecidos e fazer algo totalmente diferente) que o que um usuário regular seria de esperar)?
Olivier Dulac
O OP pediu uma maneira mais concisa de adicionar caminhos. Isso é muito mais detalhado e mais propenso a erros do que o que eles já estavam tentando evitar.
underscore_d
-1

Existe uma maneira fácil! Leia Funções de Shell e Variáveis ​​de Caminho no Linux Journal , 01 de março de 2000 Por Stephen Collyer

As funções permitem-me usar um novo tipo de dados no meu ambiente bash - a lista separada por dois pontos. Além do PATH, eu os uso para ajustar meu LOCATE_PATH, MANPATH e outros, e como um tipo de dados geral na programação do bash. Aqui está como eu configuro meu PATH (usando as funções):

# Add my bin directory to $PATH at the beginning, so it overrides 
addpath -f -p PATH $HOME/bin

# For Raspberry Pi development (add at end)
addpath -b -p PATH ${HOME}/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

# remove nonexistent directories from PATH
delpath -n -p PATH

# ensure PATH contains unique entries
uniqpath -p PATH

Como o link do Linux Journal é chamado de "quebrado", coloquei as Funções do caminho Bash em um arquivo .shar em http://pastebin.ubuntu.com/13299528/

waltinator
fonte
3
Que funções são essas? Como o OP os usa? Presumivelmente, eles estão descritos no link quebrado na sua resposta, e é exatamente por isso que sempre queremos que as respostas sejam independentes. Edite e inclua as funções reais em sua resposta.
terdon
@terdon: link funciona para mim, eu coloquei um arquivo .shar em pastebin, não vou postar 1 mil linhas aqui.
waltinator
Opa, funciona para mim também agora. Não aconteceu quando deixei o comentário. De qualquer forma, a essência do meu comentário foi que tentamos evitar links para recursos externos nas respostas. Queremos que as informações estejam aqui, onde possam ser atualizadas, editadas e imunes à podridão do link.
terdon