Estou tentando escrever uma função shell bash que me permitirá remover cópias duplicadas de diretórios da minha variável de ambiente PATH.
Disseram-me que é possível conseguir isso com um comando de uma linha usando o awk
comando, mas não consigo descobrir como fazê-lo. Alguém sabe como?
Responder1
Se você ainda não possui duplicatas no PATH
e deseja adicionar diretórios apenas se eles ainda não estiverem lá, você pode fazer isso facilmente apenas com o shell.
for x in /path/to/add …; do
case ":$PATH:" in
*":$x:"*) :;; # already there
*) PATH="$x:$PATH";;
esac
done
E aqui está um trecho de shell que remove duplicatas de arquivos $PATH
. Ele percorre as entradas uma por uma e copia aquelas que ainda não foram vistas.
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*} # the first remaining entry
case $PATH: in
*:"$x":*) ;; # already there
*) PATH=$PATH:$x;; # not there yet
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:}
unset old_PATH x
fi
Responder2
Aqui está uminteligívelsolução de uma linha que faz todas as coisas certas: remove duplicatas, preserva a ordem dos caminhos e não adiciona dois pontos no final. Portanto, ele deve fornecer um PATH desduplicado que forneça exatamente o mesmo comportamento do original:
PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"
Ele simplesmente divide em dois pontos ( split(/:/, $ENV{PATH})
), usa usos grep { not $seen{$_}++ }
para filtrar quaisquer instâncias repetidas de caminhos, exceto a primeira ocorrência, e então junta os restantes novamente separados por dois pontos e imprime o resultado ( print join(":", ...)
).
Se você quiser mais estrutura em torno disso, bem como a capacidade de desduplicar outras variáveis, tente este trecho, que estou usando atualmente em minha própria configuração:
# Deduplicate path variables
get_var () {
eval 'printf "%s\n" "${'"$1"'}"'
}
set_var () {
eval "$1=\"\$2\""
}
dedup_pathvar () {
pathvar_name="$1"
pathvar_value="$(get_var "$pathvar_name")"
deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")"
set_var "$pathvar_name" "$deduped_path"
}
dedup_pathvar PATH
dedup_pathvar MANPATH
Esse código desduplicará PATH e MANPATH, e você poderá facilmente chamar dedup_pathvar
outras variáveis que contenham listas de caminhos separados por dois pontos (por exemplo, PYTHONPATH).
Responder3
Aqui está um elegante:
printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'
Mais (para ver como funciona):
printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'
Ok, já que você é novo no Linux, aqui está como definir PATH sem um ":" final
PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`
aliás, certifique-se de NÃO ter diretórios contendo ":" em seu PATH, caso contrário, ficará confuso.
algum crédito para:
Responder4
Contanto que adicionemos oneliners não awk:
PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")
(Pode ser tão simples quanto PATH=$(zsh -fc 'typeset -U path; echo $PATH')
zsh sempre lê pelo menos um zshenv
arquivo de configuração, que pode modificar PATH
.)
Ele usa dois recursos interessantes do zsh:
- escalares vinculados a matrizes (
typeset -T
) - e matrizes que removem automaticamente valores duplicados (
typeset -U
).