Eu tenho uma função no meu .bashrc
para sudo automaticamente para abrir arquivos que não são graváveis por mim:
vim() {
if [ -w "$1" ]; then
\vim "$1"
else
sudo env HOME="$HOME" \vim -u ~/.vimrc "$1"
fi
}
Quando o arquivo precisa de sudo, funciona bem. Quando isso não acontece, ele chama recursivamente esta função e usa 100% de 1 CPU até I CC.
A partir desta resposta , vejo que existem algumas opções, todas as quais tentei. Um realmente funciona:
'vim' "$1" #fails
\vim "$1" #fails
command vim "$1" #Works!
Por que as outras opções não funcionam como eu esperava?
(Eu sei que isso é uma duplicata, mas foi muito difícil encontrar minha resposta no SO/SE com os títulos das perguntas atuais, então eu queria postar uma pergunta com um título que eu e outros poderíamos encontrar pesquisando no google)
Qualquer caractere na frente de um alias impedirá que o alias seja acionado:
No entanto, isso não interrompe as funções e você precisa
command
fazer referência ao interno subjacente se criar uma função com o mesmo nome.Explicação
O problema é que as duas primeiras opções só podem lidar com aliases. Eles não são operadores de redirecionamento especiais que podem detectar se você tem uma função ou comando com o mesmo nome. Tudo o que eles fazem é colocar algo na frente do alias, pois os aliases só se expandem se forem a primeira parte de um pipe ou comando
Bash apenas tenta expandir a primeira palavra de um comando usando a lista de aliases que ele conhece (que você pode obter com
alias
). Os aliases não aceitam argumentos e podem ser apenas a primeira palavra (separada por espaços;vim x.txt
não será expandidasudo vimim x.txt
usando o alias acima) em um comando para expandir corretamente.No entanto, a expansão nunca acontece com aspas simples:
echo '$USER'
imprimirá um literal$USER
e não o que a variável representa. Além disso, o bash se expande\x
para serx
(principalmente). Essas não são formas extras adicionadas especificamente para escapar de um alias, elas são apenas parte de como a expansão do bash foi escrita. Assim, você pode usar esses métodos para permitir que um alias sombreie um comando e ainda tenha acesso ao comando real.Porque existem alguns detalhes na forma como uma linha de comando é executada.
O conceito básico é que a primeira palavra de uma linha de comando é o comando que o shell irá pesquisar e tentar executar, nesta ordem :
A linha de comando é dividida em metacaracteres (principalmente):
Se a primeira palavra sem aspas corresponder a uma palavra de alias, ela será substituída pela definição de alias. Para evitar loops, isso é executado apenas uma vez se a nova primeira palavra corresponder a um alias que está sendo expandido. Isso faz com que isso funcione sem loops:
Observe que: Se o último caractere do valor do alias estiver em branco, a próxima palavra de comando após o alias também será verificada quanto à expansão do alias.
Se a primeira palavra (resultante) for uma expansão (como um
$var
), ela será substituída (e sujeita à divisão de palavras IFS (e globing) se não for citada):A primeira palavra resultante após as expansões acima (após atribuições de variáveis opcionais) será pesquisada nesta ordem:
$PATH
dirs)A primeira correspondência encontrada será executada.
A ordem pode ser alterada por (para teste):
\test
ou qualquer outro tipo de expansão.command test
.builtin test
./bin/test
.Assim, uma função definida como:
Será chamado em um loop infinito. Como também um script que chama a mesma primeira palavra. Algo como:
Reexecutará o mesmo script em um loop até que o kernel seja interrompido (se o kernel em uso tiver um limite de chamadas sucessivas para scripts).
A ordem será mostrada executando
type -a
:A função (conforme definido na pergunta) ignorará apenas o alias,
\vim
mas não a função. Para ignorar alias e funções, use o comando. Esta função deve fazer o que você precisa:Se você fizer a função chamar o caminho completo para o
vim
binário, não obterá o loop recursivo. Além disso, usando a-H
opção sudo define $HOME.Obrigado. Eu não tinha pensado nisso, mas agora eu tenho isso no meu
.bashrc
também.EDITADO: chamada atualizada
command vim
esudo env
em vez desudo -h
. desta forma, não há loop para ficar.