Alguém escreveu uma função bash para adicionar um diretório ao $ PATH apenas se ele ainda não estiver lá?
Eu normalmente adiciono ao PATH usando algo como:
export PATH=/usr/local/mysql/bin:$PATH
Se eu construir meu PATH em .bash_profile, ele não será lido, a menos que a sessão em que eu participe seja uma sessão de logon - o que nem sempre é verdade. Se eu construir meu PATH no .bashrc, ele será executado com cada subshell. Portanto, se eu iniciar uma janela do Terminal e, em seguida, executar a tela e, em seguida, executar um script de shell, obtenho:
$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....
Vou tentar criar uma função bash chamada add_to_path()
que apenas adiciona o diretório se ele não estiver lá. Mas, se alguém já escreveu (ou encontrou) uma coisa dessas, não vou dedicar tempo a isso.
Respostas:
Do meu .bashrc:
Observe que PATH já deve estar marcado como exportado, portanto, a reexportação não é necessária. Isso verifica se o diretório existe e é um diretório antes de adicioná-lo, com o qual você pode não se importar.
Além disso, isso adiciona o novo diretório ao final do caminho; para colocar no início, use em
PATH="$1${PATH:+":$PATH"}"
vez daPATH=
linha acima .fonte
":$PATH:"
em vez de apenas"$PATH"
${PATH:+"$PATH:"}
$ 1"${variable:+value}
significa verificar sevariable
está definido e tem um valor não vazio e se fornece o resultado da avaliaçãovalue
. Basicamente, se PATH não for em branco, ele será definido como"$PATH:$1"
; se estiver em branco, será definido apenas"$1"
(observe a falta de dois pontos).Expandindo a resposta de Gordon Davisson, isso suporta vários argumentos
Assim, você pode fazer o caminho: path1 path2 path3 ...
Para anexar,
Semelhante ao pathappend, você pode fazer
pathprepend path1 path2 path3 ...
fonte
pathprepend P1 P2 P3
e terminar comPATH=P1:P2:P3
. Para obter esse comportamento, alterefor ARG in "$@" do
parafor ((i=$#; i>0; i--)); do ARG=${!i}
Aqui está algo da minha resposta a esta pergunta combinada com a estrutura da função de Doug Harris. Ele usa expressões regulares do Bash:
fonte
$1
vez de #${1}
Coloque isso nos comentários da resposta selecionada, mas os comentários parecem não suportar a formatação PRE, portanto adicione a resposta aqui:
@ gordon-davisson Eu não sou um grande fã de citações e concatenações desnecessárias. Supondo que você esteja usando uma versão do bash> = 3, você pode usar regexs integrados do bash e fazer:
Isso manipula corretamente os casos em que há espaços no diretório ou no PATH. Há alguma dúvida sobre se o mecanismo de regex do bash é lento o suficiente para que isso possa ser menos eficiente do que a concatenação e interpolação de strings que sua versão faz, mas de alguma forma parece mais esteticamente limpo para mim.
fonte
formatting using the backtick
apenas, mas você não obtém nenhum controle de parágrafo decente.unnecessary quoting
implica que você sabe com antecedência que$PATH
não é nulo."$PATH"
torna OK se PATH é nulo ou não. Da mesma forma, se$1
contém caracteres que podem confundir o analisador de comandos. Colocar o regex entre aspas"(^|:)$1(:|$)"
impede isso.[[
e]]
. Eu prefiro citar tudo o que pode possivelmente precisam ser citado, a menos que faz com que ele falhar, mas eu acredito que ele está certo, e que cita realmente não são necessários ao redor$PATH
. Por outro lado, parece-me que você está certo$1
.Quando você precisar que $ HOME / bin apareça exatamente uma vez no início do seu $ PATH e em nenhum outro lugar, não aceite substitutos.
fonte
PATH=${PATH/"
... em vez dePATH=${PATH//"
... para fazê-la funcionar.$1
é a única entrada (sem dois pontos). A entrada fica dobrada.Aqui está uma solução alternativa que tem a vantagem adicional de remover entradas redundantes:
O argumento único para essa função é anexado ao PATH e a primeira instância da mesma sequência é removida do caminho existente. Em outras palavras, se o diretório já existir no caminho, ele será promovido para a frente em vez de adicionado como duplicado.
A função funciona adicionando dois pontos ao caminho para garantir que todas as entradas tenham dois pontos à frente e, em seguida, acrescentando a nova entrada ao caminho existente com a entrada removida. A última parte é executada usando a
${var//pattern/sub}
notação do bash ; veja o manual do bash para mais detalhes.fonte
/home/robert
seuPATH
e vocêpathadd /home/rob
.Aqui está o meu (acredito que foi escrito anos atrás por Oscar, o administrador de sistemas do meu antigo laboratório, tudo creditado a ele), está no meu bashrc há séculos. Ele tem o benefício adicional de permitir que você acrescente ou anexe o novo diretório conforme desejado:
Uso:
fonte
Para anexar, eu gosto da solução de Russell, mas há um pequeno erro: se você tentar anexar algo como "/ bin" a um caminho de "/ sbin: / usr / bin: / var / usr / bin: / usr / local / bin: / usr / sbin "substitui" / bin: "3 vezes (quando realmente não correspondia). Combinando uma correção para isso com a solução anexa de @ gordon-davisson, recebo o seguinte:
fonte
Um alias simples como este abaixo deve fazer o truque:
Tudo o que faz é dividir o caminho no caractere: e comparar cada componente com o argumento que você passa. O grep verifica se há uma correspondência completa de linhas e imprime a contagem.
Uso da amostra:
Substitua o comando echo por addToPath ou algum apelido / função semelhante.
fonte
Consulte Como evitar duplicar a variável de caminho no csh? no StackOverflow para obter um conjunto de respostas para esta pergunta.
fonte
Aqui está o que eu criei:
Agora em .bashrc eu tenho:
Versão atualizada após o comentário sobre como meu original não manipula diretórios com espaços (obrigado a esta pergunta por me indicar o uso
IFS
):fonte
PATH
contém espaços em branco,*
,?
, ou[
...]
.Documents and Settings
eProgram Files
), mas o Unix suporta nomes de caminho que contêm espaços em branco desde antes da existência do Windoze.Estou um pouco surpreso que ninguém tenha mencionado isso ainda, mas você pode usar
readlink -f
para converter caminhos relativos em caminhos absolutos e adicioná-los ao PATH como tal.Por exemplo, para melhorar a resposta de Guillaume Perrault-Archambault,
torna-se
1. O básico - que bem isso faz?
O
readlink -f
comando (entre outras coisas) converterá um caminho relativo em um caminho absoluto. Isso permite que você faça algo como2. Por que testamos duas vezes o CAMINHO?
Bem, considere o exemplo acima. Se o usuário disser a partir do diretório uma segunda vez, será . Claro, não estará presente em . Mas, em seguida, será definido como (o equivalente caminho absoluto ), que é já . Então, precisamos evitar a adição de uma segunda vez.
pathappend .
/path/to/my/bin/dir
ARG
.
.
PATH
ARGA
/path/to/my/bin/dir
.
PATH
/path/to/my/bin/dir
PATH
Talvez o mais importante
readlink
seja que, como o nome indica , o objetivo principal é olhar para um link simbólico e ler o nome do caminho que ele contém (ou seja, aponta para). Por exemplo:Agora, se você diz
pathappend /usr/lib/perl/5.14
, e você já tem/usr/lib/perl/5.14
no seu CAMINHO, tudo bem; podemos simplesmente deixar como está. Mas, se/usr/lib/perl/5.14
já não estiver no seu PATH, chamamosreadlink
e obtemosARGA
=/usr/lib/perl/5.14.2
e, em seguida, adicionamos isso aPATH
. Mas espere um minuto - se você já dissepathappend /usr/lib/perl/5.14
, então você já tem/usr/lib/perl/5.14.2
seu PATH e, novamente, precisamos verificar isso para evitar adicioná-lo aPATH
uma segunda vez.3. Qual é o problema
if ARGA=$(readlink -f "$ARG")
?Caso não esteja claro, essa linha testa se o resultado
readlink
é bem - sucedido. Isso é apenas uma boa prática de programação defensiva. Se vamos usar a saída do comando m como parte do comando n (onde m < n ), é prudente verificar se o comando m falhou e lidar com isso de alguma forma. Eu não acho que issoreadlink
irá falhar - mas, como discutido em Como recuperar o caminho absoluto de um arquivo arbitrário do OS X e de outros lugares,readlink
é uma invenção do GNU. Ele não está especificado no POSIX, portanto, sua disponibilidade no Mac OS, Solaris e outros Unixes não Linux é questionável. Na verdade, acabei de ler um comentário que diz "readlink -f
parece não funcionar no Mac OS X 10.11.6, masrealpath
funciona imediatamente . ”Portanto, se você estiver em um sistema que não possuireadlink
ou ondereadlink -f
não funciona, poderá modificá-lo script a ser usadorealpath
.) Ao instalar uma rede de segurança, tornamos nosso código um pouco mais portátil.Claro, se você estiver em um sistema que não possui
readlink
(ourealpath
), não vai querer fazer isso .pathappend .
O segundo
-d
teste ([ -d "$ARGA" ]
) é realmente provavelmente desnecessário. Não consigo pensar em nenhum cenário em que$ARG
um diretório sejareadlink
bem - sucedido, mas$ARGA
não seja um diretório. Apenas copiei e colei a primeiraif
declaração para criar a terceira e deixei o-d
teste por preguiça.4. Outros comentários?
Sim. Como muitas das outras respostas aqui, esta testa se cada argumento é um diretório, o processa se for e o ignora se não for. Isso pode (ou não) ser adequado se você estiver usando
pathappend
apenas.
arquivos " " (como.bash_profile
e.bashrc
) e outros scripts. Mas, como essa resposta mostrou (acima), é perfeitamente viável usá-la interativamente. Você ficará muito confuso se fizer issoComo eu disse acima, é uma boa prática lidar com erros. Aqui está um exemplo:
fonte
readlink -f "$ARG"
. (2) Não sei por que isso aconteceria (especialmente depois que o-d "$ARG"
teste foi bem-sucedido), mas você pode querer testar sereadlink
falhou. (3) Você parece ignorarreadlink
a função principal - mapear um nome de link simbólico para um "nome real do arquivo". Se (por exemplo)/bin
for um link simbólico para/bin64
, chamadas repetidas parapathappend /bin
poderão resultar em dizer PATH…:/bin64:/bin64:/bin64:/bin64:…
. Você provavelmente deve (também) verificar se o novo valor$ARG
já está emPATH
..
no meu exemplo, acredito que possa ser considerado um nome de arquivo canônico. Corrigir?readlink
o nome implica claramente seu objetivo - ele olha para um link simbólico e lê o nome do caminho que ele contém (ou seja, aponta para). (2) Bem, eu disse que não sabia por quereadlink
iria falhar. Eu estava afirmando que, se um script ou função contém vários comandos, e o comando n é garantido como falha catastroficamente (ou não faz sentido) se o comando m falhar (onde m < n ), é uma boa prática prudente verifique se o comando m falhou e lide com isso de alguma maneira - pelo menos, ... (continua)readlink
poderá falhar se (a) o diretório for renomeado ou excluído (por outro processo) entre suas chamadas paratest
ereadlink
, ou (b) se/usr/bin/readlink
for excluído (ou corrompido). (3) Você parece estar perdendo o meu argumento. Não estou incentivando você a duplicar outras respostas; Estou dizendo que, verificando se o originalARG
(na linha de comando) já está dentroPATH
, mas não repetindo a verificação do novoARG
(a saída dereadlink
), sua resposta está incompleta e, portanto, incorreta. ... (continua)fonte
Desta forma, funciona bem:
fonte
Minhas versões são menos cuidadosas com caminhos vazios e insistindo em que os caminhos sejam válidos e diretórios do que alguns postados aqui, mas eu acho uma coleção grande de prefpend / append / clean / unique-ify / etc. funções shell para serem úteis na manipulação de caminhos. O lote inteiro, em seu estado atual, está aqui: http://pastebin.com/xS9sgQsX (feedback e melhorias muito bem-vindos!)
fonte
Você pode usar um liner perl one:
Aqui está no bash:
fonte
Modifiquei levemente a resposta de Gordon Davisson para usar o dir atual, se nenhum for fornecido. Então você pode simplesmente fazer a
padd
partir do diretório que deseja adicionar ao seu PATH.fonte
Você pode verificar se uma variável personalizada foi definida, caso contrário, defina-a e adicione as novas entradas:
Obviamente, essas entradas ainda poderão ser duplicadas se adicionadas por outro script, como
/etc/profile
.fonte
Este script permite adicionar no final de
$PATH
:Ou adicione no início de
$PATH
:fonte
Aqui está uma maneira compatível com POSIX.
É extraído da resposta de Guillaume Perrault-Archambault a esta pergunta e da resposta de mike511 aqui .
UPDATE 2017-11-23: Bug corrigido por @Scott
fonte
/etc/profile
. O diretório que você está tentando adicionar ao PATH já pode estar lá mais de uma vez , e seu código é bloqueado por isso. ... (continua)/a/ck
a/b:/a/ck:/tr:/a/ck
, você recebe/a/ck:/b:/a/ck:/tr:/tr:/a/ck
.