Por quê !! dentro de um alias não funciona?

5

Eu tenho esse alias definido no meu sistema /etc/bashrc file:

alias root="sudo !!"

A intenção disso é executar o último comando usado usando sudo , é claro. Quando usado, é claro que parece substituir o último comando em history pelo arquivo bashrc na inicialização do shell, e não o comando real que você obteria se estivesse executando sudo !! em um shell interativo. Eu também tentei alias root="sudo fc -s" sem sucesso.

Eu percebo que isso é provavelmente algo a ver com o modo como o BASH comanda substituições, mas alguém pode explicar por que isso é, e fornecer um substituto utilizável?

Estou executando o BASH versão 3.2.51 (1) -release (x86_64-apple-darwin13).

    
por xanadu 05.12.2013 / 12:29

2 respostas

6

A parte chave neste comportamento é explicada por 2 bits na página de manual bash:

Na seção HISTORY EXPANSION :

History expansion is performed immediately after a complete line is read, before the shell breaks it into words.

Na seção ALIASES :

The first word of each simple command, if unquoted, is checked to see if it has an alias.

Portanto, basicamente, a expansão do histórico ocorre antes da divisão de palavras. A expansão do alias ocorre depois.

Solução alternativa

A melhor maneira que posso pensar em fazer isso é o seguinte:

alias root='sudo $(fc -ln -1)'

Isso funcionará para comandos simples, mas para comandos mais complexos, precisamos ajustá-lo.

alias root='sudo sh -c "$(fc -ln -1)"'

O motivo da mudança é por causa de algo assim:

# alias root='sudo $(fc -ln -1)'
# echo "I AM $(whoami)"
I AM patrick
# root
"I AM $(whoami)"

Como você pode ver, nenhuma análise de shell é feita. Ao alterá-lo para usar sh -c "..." , ele faz a interpretação da shell.

# alias root='sudo sh -c "$(fc -ln -1)"'
# echo "I AM $(whoami)"
I AM patrick
# root
I AM root

.

Outra maneira (eu pensei nisso primeiro, então mantenha-o na resposta, mas não é tão bom quanto o acima):

alias root='fc -e "sed -i -e \"s/^/sudo /\""'

O comando fc -e executará o comando especificado passando um arquivo contendo o comando executado anteriormente. Apenas executamos sed nesse arquivo para prefixar o comando com sudo .

    
por 05.12.2013 / 14:05
2

Você não pode usar um apelido dessa maneira. Os aliases não podem receber parâmetros como !! . Para conseguir o que você quer, você poderia usar uma função.

function root() {
  sudo $(history | tail -2 | head -1 | awk '{$1=$2=$3=""; print $0}');
}

Esta é uma ideia aproximada e pode ter alguns problemas. Minha saída do comando history tem esta aparência:

$ history
1081  20131205 08:00:12  ls
1082  20131205 08:00:13  history

Então, preciso analisar essa saída. Acima eu estou correndo histórico tendo os últimos 2 comandos, em seguida, tendo o primeiro dos últimos 2 Este é o comando executado anteriormente. Eu então uso awk para me livrar das primeiras 4 colunas, deixando-nos com a linha de comando que foi executada anteriormente.

Como um alias?

Como estamos usando a saída de history , não há mais motivo para usar uma função Bash. Isso é necessário se você estiver tentando passar o comando anterior como um argumento, mas estamos obtendo-o por meio do comando history agora.

$ alias root="sudo \$(history | tail -2 | head -1 | awk '{\=\=\=\"\"; print \
function root() {
  sudo $(history | tail -2 | head -1 | awk '{$1=$2=$3=""; print $0}');
}
}')"

Além do escape mais complicado necessário para colocar isso em um alias, os trabalhos acima, semelhantes à função.

    
por 05.12.2013 / 14:05