我经常克隆一个 git 存储库,然后进入它的根目录。例如:
$ git clone https://github.com/hpjansson/chafa && cd chafa
为了让这更容易一点,我有一个 zsh 缩写 – cc
– 扩展为&& 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
!#
指当前命令行,如man zshexpn
(section HISTORY EXPANSION
, subsection Event Designators
) 中所述:
!#
参考目前输入的命令行。该行被视为完整,直到包含!#
引用的单词之前的单词。
:2
指的是命令行上的第二个单词(这是我键入$ git clone
命令时的 url):
n
第 n 个参数。
并:t
指那个词的尾巴:
t
删除所有前导路径名组件,留下尾部。这就像basename
.
通常,我会git clone
在命令行上键入,然后复制粘贴项目的 url,然后插入一个空格cc
和另一个空格,这会给出所需的命令。
但是,有时,我复制粘贴的 url 以扩展名结尾.git
。当发生这种情况时,扩展!#:2:t
包含一个不需要的.git
扩展:
$ 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
一种解决方案是也使用:r
修饰符来删除.git
扩展名:
r
删除保留根名称的文件扩展名。没有文件扩展名的字符串不会被更改。文件扩展名.
后面是任意数量的字符(包括零),它们既不是.
也不是/
,并且继续到字符串的末尾。例如,扩展名foo.orig.c
是.c
,并且dir.c/foo
没有扩展名。
vv
$ git clone https://github.com/hpjansson/chafa.git && cd !#:2:t:r
→
$ git clone https://github.com/hpjansson/chafa.git && cd chafa
但是,如果路径不以.git
扩展名结尾,则扩展失败并且git
命令和cd
命令都不会被执行。
这是一个说明问题的最小示例:
$ echo /foo/bar.baz !#:1:t:r
/foo/bar.baz bar
$ echo /foo/bar !#:1:t:r
zsh: modifier failed: r
第一个命令成功,因为最后一个路径组件包含扩展名.baz
,但第二个命令失败,因为不再有扩展名。OTOH,在 bash 中4.3.48
,两个命令都按预期工作。
我不明白为什么:r
没有扩展时会失败,因为它的文档包含这句话:
没有文件扩展名的字符串不会被更改。
它并不是说使用:r
不包含扩展名的字符串是错误的。
我尝试了另一种方法;:s
使用修饰符删除扩展名。我认为这需要设置HIST_SUBST_PATTERN
选项:
setopt HIST_SUBST_PATTERN
使用此选项,可以编写:s/.*
删除扩展名:
vvvvv
$ echo /foo/bar.baz !#:1:t:s/.*
/foo/bar.baz bar
它在有扩展名时有效,但在没有扩展名时再次失败:
$ setopt HIST_SUBST_PATTERN
$ echo /foo/bar !#:1:t:s/.*
zsh: substitution failed
是否有一个修饰符序列可以同时引用bar
in/foo/bar.baz
和 to bar
in /foo/bar
?
我正在使用zsh 5.7.1-dev-0 (x86_64-pc-linux-gnu)
.
我想不出一种通过历史扩展来做到这一点的方法。但我不认为历史扩展是这里最好的工具。这是非常有限的。从zle 小部件,您可以访问命令行并使用任意代码对其进行操作。利用这一点。
一种方法是对缩写进行扩展:而不是
$abbrev[$MATCH]
使用${(e)abbrev[$MATCH]}
. 显然,您需要更改缩写以适当地引用特殊字符。对于cc
, 使用类似的东西对于 的特定情况
git clone
,更一般地,对于在当前目录中创建目录的任何内容,您可以使用不同的缩写:切换到新创建的目录。这适用于git clone https://example.com/foo.git
、 forgit clone https://example.com/foo
和 forgit clone https://example.com/foo.git bar
,以及 fortar xf foo.tar
(如果存档有一个顶级目录)和mv /some/directory .
.