Veja! A função industrial de 12 linhas ... tecnicamente bash e zsh-portável que adora o seu% script de inicialização~/.bashrc
ou ~/.zshrc
de escolha:
# void +path.append(str dirname, ...)
#
# Append each passed existing directory to the current user's ${PATH} in a
# safe manner silently ignoring:
#
# * Relative directories (i.e., *NOT* prefixed by the directory separator).
# * Duplicate directories (i.e., already listed in the current ${PATH}).
# * Nonextant directories.
+path.append() {
# For each passed dirname...
local dirname
for dirname; do
# Strip the trailing directory separator if any from this dirname,
# reducing this dirname to the canonical form expected by the
# test for uniqueness performed below.
dirname="${dirname%/}"
# If this dirname is either relative, duplicate, or nonextant, then
# silently ignore this dirname and continue to the next. Note that the
# extancy test is the least performant test and hence deferred.
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
# Else, this is an existing absolute unique dirname. In this case,
# append this dirname to the current ${PATH}.
PATH="${PATH}:${dirname}"
done
# Strip an erroneously leading delimiter from the current ${PATH} if any,
# a common edge case when the initial ${PATH} is the empty string.
PATH="${PATH#:}"
# Export the current ${PATH} to subprocesses. Although system-wide scripts
# already export the ${PATH} by default on most systems, "Bother free is
# the way to be."
export PATH
}
Prepare-se para a glória instantânea. Então, em vez de fazer isso e desejar o melhor:
export PATH=$PATH:~/opt/bin:~/the/black/goat/of/the/woods/with/a/thousand/young
Faça isso e tenha a garantia de obter o melhor, quer realmente queira ou não:
+path.append ~/opt/bin ~/the/black/goat/of/the/woods/with/a/thousand/young
Muito bem, defina "melhor".
Anexar e prefixar com segurança o ${PATH}
atual não é o assunto trivial que costuma ser. Embora seja conveniente e aparentemente sensato, os marcadores da forma export PATH=$PATH:~/opt/bin
apresentam complicações diabólicas:
-
Dirnames relativos a um acidente (por exemplo, export PATH=$PATH:opt/bin
). Enquanto bash
e zsh
silenciosamente aceitam e ignoram principalmente os dirnames relativos em mais casos, os nomes de diretório relativos prefixados por h
ou t
(e possivelmente outros caracteres nefastos) fazem com que ambos vergonhosamente mutilem a obra-prima seminal de 1962 do ala Masaki Kobayashi > Harakiri :
# Don't try this at home. You will feel great pain.
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:harakiri && echo $PATH
/usr/local/bin:/usr/bin:arakiri
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:tanuki/yokai && echo $PATH
binanuki/yokai # Congratulations. Your system is now face-up in the gutter.
-
Dircames acidentalmente duplicados. Embora% duplicado${PATH}
dirnames sejam amplamente inócuos, eles também são indesejados, incômodos, levemente ineficientes, impedem a depuração e promovem o desgaste da unidade - sorta como essa resposta . Enquanto os SSDs estilo NAND são ( é claro ) imunes ao uso de leitura, os HDDs não são. O acesso desnecessário ao sistema de arquivos em todo comando tentado implica desgaste desnecessário da cabeça de leitura no mesmo andamento. As duplicatas são particularmente untuosas quando invocam shells aninhados em subprocessos aninhados, ponto no qual aparentemente one-liners inofensivos como export PATH=$PATH:~/wat
rapidamente explodem no Sétimo Círculo de ${PATH}
Inferno como PATH=/usr/local/bin:/usr/bin:/bin:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat
. Apenas Beelzebubba pode ajudá-lo se você adicionar mais nomes para isso. ( Não deixe que isso aconteça com seus filhos preciosos. )
-
Faltam nomes acidentalmente. Novamente, embora a falta de
${PATH}
dirnames seja bastante inofensiva, eles também são geralmente indesejados, incômodos, levemente ineficientes, impedem a depuração e promovem o desgaste da unidade.
Ergo, automação amigável como a função shell definida acima. Devemos nos salvar de nós mesmos.
Mas ... Por que "+ path.append ()"? Por que não simplesmente append_path ()?
Por falta de ambiguidade (por exemplo, com comandos externos no atual ${PATH}
ou funções de shell em todo o sistema definidas em outro lugar), as funções do shell definidas pelo usuário são idealmente prefixadas ou sufixadas com substrings exclusivas suportadas por bash
e zsh
de outra forma proibida para nomes de comando padrão - como, por exemplo, +
.
Ei. Funciona. Não me julgue.
Mas ... Por que "+ path.append ()"? Por que não "+ path.prepend ()"?
Como anexar ao atual ${PATH}
é mais seguro do que prefixar ao atual ${PATH}
, sendo todas as coisas iguais, o que elas nunca são. Substituir comandos de todo o sistema por comandos específicos do usuário pode ser, na melhor das hipóteses, insalubre e, na pior, maluco. No Linux, por exemplo, os aplicativos downstream geralmente esperam as variantes de comandos GNU coreutils em vez de derivativos ou alternativas não-padrão personalizados.
Dito isso, há casos de uso válidos para isso. Definir a função +path.prepend()
equivalente é trivial. Nebulosa Sans prolix, por sua sanidade compartilhada:
+path.prepend() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
PATH="${dirname}:${PATH}"
done
PATH="${PATH%:}"
export PATH
}
Mas ... Por que não Gilles?
Gilles ' aceite resposta em outro lugar é impressionantemente ideal no caso geral como um " shell idempotent agnóstico ". No caso comum de bash
e zsh
com não links simbólicos indesejáveis, no entanto, a penalidade de desempenho necessária para fazê-lo entristece o Gentoo ricer em mim. Mesmo na presença de links simbólicos indesejáveis, é discutível se forçar um subshell por add_to_PATH()
argumento vale a inserção potencial de duplicatas symlink.
Para casos de uso rigoroso que exigem que até mesmo as duplicatas de links simbólicos sejam eliminadas, essa variante zsh
-específica faz isso por meio de builtins eficientes em vez de forquilhas ineficientes:
+path.append() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname:A}:"* &&
-d "${dirname}" ]] || continue
PATH="${PATH}:${dirname}"
done
PATH="${PATH#:}"
export PATH
}
Observe o *":${dirname:A}:"*
em vez de *":${dirname}:"*
do original. :A
é um zsh
-ísmo maravilhoso ausente na maioria das outras camadas - incluindo bash
. Para citar man zshexpn
:
A: Turn a file name into an absolute path as the a
modifier does, and then pass the result through the realpath(3)
library function to resolve symbolic links. Note: on systems that do not have a realpath(3)
library function, symbolic links are not resolved, so on those systems a
and A
are equivalent.
Sem mais perguntas.
De nada. Desfrute de bombardeios seguros. Você agora merece isso.