Como eu substituo um argumento específico de um comando anterior em zsh?

2

Digamos que eu tenha o seguinte comando:

/long/path/to/bin -a foo -b 2 -c 3 -d 4 foo | gunzip -c | less

E agora quero substituir foo por trás de 4 por bar , por isso se torna:

/long/path/to/bin -a foo -b 2 -c 3 -d 4 bar | gunzip -c | less

Eu também sei que eu poderia fazer:

!:0-8 bar !:10-$

Existe também uma maneira pela qual eu possa simplesmente substituir um argumento? Algo como:

!:9:bar

Obviamente, isso não vai funcionar, porque ela só vai expandir-se para o 9 th argumento, reclamando um modificador desconhecido.

    
por exhuma 02.06.2014 / 11:42

1 resposta

2

Você pode usar um widget zle para fazer isso.

Defina o widget

Digite isto na linha de comando (ou insira-o no seu ~/.zshrc , então é permanente):

accept-line-with-histmod() {
  if [[ $BUFFER[1,3] == "?\!:" ]]; then
    local loc mod
    loc=${${(s.:.)BUFFER}[2]}
    mod=${${(s.:.)BUFFER}[3]}
    zle up-history
    BUFFER="${${(z)BUFFER}[1,$((loc))]} $mod ${${(z)BUFFER}[$((loc+2)),-1]}"
    CURSOR=$#BUFFER
  else
    zle accept-line
  fi
}
zle -N accept-line-with-histmod
bindkey "^M" accept-line-with-histmod

Como usar?

A sintaxe é um pouco diferente da que você propôs, mas não muito (adicionei um ponto de interrogação na frente, para que a análise seja mais fácil). Demo ( $ é o prompt):

$ /long/path/to/bin -a foo -b 2 -c 3 -d 4 foo | gunzip -c | less
[...]
$ ?!:9:bar
$ /long/path/to/bin -a foo -b 2 -c 3 -d 4 bar | gunzip -c | less

Como funciona!

Este widget está ligado à chave ENTER , substituindo a ligação de chave padrão de accept-line , portanto, ele é executado toda vez que você pressiona entrar.

Primeiro, o widget verifica se a linha de comando começa com ?!: ; a variável $BUFFER contém a linha de comando completa. Se não , o widget usual accept-line é executado, resultando no comportamento padrão.

Mas se a linha começar com ?!: , os argumentos ( ?!:POSITION:REPLACEMENT TEXT ) serão armazenados nas variáveis $loc e $mod , resp. [$ {$ {(s.:.)BUFFER}[2]} divide a linha de comando ( BUFFER ) em dois pontos ( (s.:.) ) e pega a segunda palavra ( [2] )]

Agora, ele relembra a última linha do histórico ( zle up-history ) que agora reside em $BUFFER . (Se você quiser lidar com eventos mais antigos, então você deve expandir o widget aqui!)

Então basicamente faz o mesmo que você fez no seu manual, construindo a linha de comando atual da antiga. (O deslocamento de 1 é porque o índice de matrizes começa em 1, mas o índice na expansão de histórico em 0).

Finalmente, o cursor é colocado no final da nova linha de comando, esperando que você confirme a execução.

Observações : Obviamente, não há tratamento de erros, então tenha cuidado! Eu também não testei com linhas de comando muito complexas. Pode ou não trabalhar com eles.

Como alternativa, você pode estar interessado na pesquisa & substitua a sintaxe da expansão do histórico:

$ echo foo
$ !:s/foo/bar

Ou o r incorporado

$ echo foo
$ r foo=bar

No entanto, isso não é possível com o seu exemplo na pergunta, já que não existe um único.

    
por 02.06.2014 / 18:55