Costumo clonar um repositório git e entrar em seu diretório raiz. Por exemplo:
$ git clone https://github.com/hpjansson/chafa && cd chafa
Para tornar isso um pouco mais fácil, tenho uma abreviação zsh – cc
– que é expandida para && cd !#:2:t
:
typeset -Ag abbrev
abbrev=(
'G' '| grep -v grep | grep'
'L' '2>&1 | less'
'V' '2>&1 | vipe >/dev/null'
'ac' '| column -t'
'bl' '; tput bel'
'cc' '&& cd !#:2:t'
'fl' "| awk '{ print $"
'ne' '2>/dev/null'
'pf' "printf -- '"
'sl' '>/dev/null 2>&1'
'tl' '| tail -20'
)
__abbrev_expand() {
emulate -L zsh
setopt EXTENDED_GLOB
local MATCH
LBUFFER=${LBUFFER%%(#m)[a-zA-Z]#}
if [[ "${LBUFFER: -1}" == ' ' ]]; then
LBUFFER+=${abbrev[$MATCH]:-$MATCH}
if [[ $MATCH = 'fl' ]]; then
RBUFFER="}'"
elif [[ $MATCH = 'pf' ]]; then
RBUFFER="\n'"
fi
else
LBUFFER+=$MATCH
fi
zle self-insert
}
zle -N __abbrev_expand
bindkey ' ' __abbrev_expand
bindkey -M isearch ' ' self-insert
!#
refere-se à linha de comando atual, conforme descrito em man zshexpn
(seção HISTORY EXPANSION
, subseção Event Designators
):
!#
Consulte a linha de comando atual digitada até agora. A linha é tratada como se estivesse completa até e incluindo a palavra anterior àquela com a!#
referência.
:2
refere-se à segunda palavra na linha de comando (que é a url quando digito um $ git clone
comando):
n
O enésimo argumento.
E :t
se refere à cauda dessa palavra:
t
Remova todos os componentes principais do nome do caminho, deixando a cauda. Isso funciona comobasename
.
Normalmente, digito git clone
na linha de comando, copio e colo a url do projeto e insiro um espaço cc
e outro espaço, que fornece o comando desejado.
No entanto, às vezes, o URL que eu copio e colo termina com a extensão .git
. Quando isso acontece, a expansão de !#:2:t
contém uma .git
extensão indesejável:
$ git clone https://github.com/hpjansson/chafa.git cc
→
$ git clone https://github.com/hpjansson/chafa.git && cd !#:2:t
→
$ git clone https://github.com/hpjansson/chafa.git && cd chafa.git
cd: no such file or directory: chafa.git
Uma solução é também usar o :r
modificador para remover a .git
extensão:
r
Remova uma extensão de nome de arquivo deixando o nome raiz. Strings sem extensão de nome de arquivo não são alteradas. Uma extensão de nome de arquivo é.
seguida por qualquer número de caracteres (incluindo zero) que não são nem.
nem/
e que continuam até o final da string. Por exemplo, a extensão defoo.orig.c
é.c
, edir.c/foo
não tem extensão.
vv
$ git clone https://github.com/hpjansson/chafa.git && cd !#:2:t:r
→
$ git clone https://github.com/hpjansson/chafa.git && cd chafa
No entanto, se o caminho não terminar com uma .git
extensão, a expansão falhará e nem o git
comando nem o cd
comando serão executados.
Aqui está um exemplo mínimo para ilustrar o problema:
$ echo /foo/bar.baz !#:1:t:r
/foo/bar.baz bar
$ echo /foo/bar !#:1:t:r
zsh: modifier failed: r
O primeiro comando é bem-sucedido porque o último componente do caminho contém a extensão .baz
, mas o segundo falha porque não há mais extensão. OTOH, em bash 4.3.48
, ambos os comandos funcionam conforme o esperado.
Não entendo por que :r
falha quando não há extensão, pois sua documentação contém esta frase:
Strings sem extensão de nome de arquivo não são alteradas.
Não diz que usar :r
para uma string que não contém uma extensão é um erro.
Tentei outra abordagem; removendo a extensão com o :s
modificador. Eu acho que isso requer que a HIST_SUBST_PATTERN
opção seja definida:
setopt HIST_SUBST_PATTERN
Com esta opção, pode-se escrever :s/.*
para remover uma extensão:
vvvvv
$ echo /foo/bar.baz !#:1:t:s/.*
/foo/bar.baz bar
Funciona quando há uma extensão, mas novamente falha quando não há nenhuma:
$ setopt HIST_SUBST_PATTERN
$ echo /foo/bar !#:1:t:s/.*
zsh: substitution failed
Existe uma única sequência de modificadores que podem se referir simultaneamente a bar
in /foo/bar.baz
e a bar
in /foo/bar
?
estou usando zsh 5.7.1-dev-0 (x86_64-pc-linux-gnu)
.
Não consigo pensar em uma maneira de fazer isso com a expansão da história. Mas não acho que a expansão da história seja a melhor ferramenta aqui. É muito limitado. A partir de um widget zle , você pode acessar a linha de comando e manipulá-la com código arbitrário. Aproveite isso.
Uma abordagem seria expandir a abreviação: em vez de
$abbrev[$MATCH]
, use${(e)abbrev[$MATCH]}
. Obviamente, você precisará alterar suas abreviações para citar caracteres especiais adequadamente. Paracc
, use algo comoPara o caso específico de
git clone
, e mais geralmente para qualquer coisa que crie um diretório no diretório atual, você pode usar uma abreviação diferente: mude para o diretório recém-criado. Isso funciona comgit clone https://example.com/foo.git
, paragit clone https://example.com/foo
e paragit clone https://example.com/foo.git bar
, bem como paratar xf foo.tar
(se o arquivo tiver um único diretório de nível superior) emv /some/directory .
.