![如何在 zsh 中替换前一个命令的特定参数?](https://linux22.com/image/1410656/%E5%A6%82%E4%BD%95%E5%9C%A8%20zsh%20%E4%B8%AD%E6%9B%BF%E6%8D%A2%E5%89%8D%E4%B8%80%E4%B8%AA%E5%91%BD%E4%BB%A4%E7%9A%84%E7%89%B9%E5%AE%9A%E5%8F%82%E6%95%B0%EF%BC%9F.png)
假设我有以下命令:
/long/path/to/bin -a foo -b 2 -c 3 -d 4 foo | gunzip -c | less
现在我想用 替换foo
后面4
的bar
,因此它变成:
/long/path/to/bin -a foo -b 2 -c 3 -d 4 bar | gunzip -c | less
我也知道我可以这样做:
!:0-8 bar !:10-$
有没有办法可以简单地替换一个参数?例如:
!:9:bar
显然这不会起作用,因为它只会扩展到第 9 个参数,并抱怨未知的修饰符。
答案1
您可以使用 zle 小部件来执行此操作。
定义小部件
在命令行中输入此内容(或将其插入到您的中~/.zshrc
,然后它就会永久生效):
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
如何使用它?
语法与你提出的略有不同,但差别不大(我在前面添加了一个问号,以便更容易解析)。演示($
是提示):
$ /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
怎么运行的!
此小部件与 键绑定ENTER
,取代 的默认键绑定accept-line
,因此每次按回车键时都会执行它。
首先,小部件检查命令行是否以 开头?!:
;变量$BUFFER
保存完整的命令行。如果不是,accept-line
将执行常见的小部件,从而导致默认行为。
但是如果该行以 开头?!:
,则参数 ( ?!:POSITION:REPLACEMENT TEXT
) 分别存储在变量$loc
和中。[${${(s.:.)BUFFER}[2]}在冒号$mod
( ) 处拆分命令行( ) 并取第二个单词 ( ) ]BUFFER
(s.:.)
[2]
现在,它从历史记录 () 中调用最后一行,zle up-history
该行现在位于 中$BUFFER
。(如果您想处理较旧的事件,则必须在此处扩展小部件!)
然后它基本上执行您在手册示例中所做的相同操作,从旧命令行构建当前命令行。(偏移量为 1 是因为数组的索引从 1 开始,但历史扩展中的索引从 0 开始)。
最后光标放在新建的命令行的末尾,等待您确认执行。
评论:显然没有错误处理,所以要小心!我也没有用非常复杂的命令行测试它。它可能对这些命令行有效,也可能无效。
或者您可能对历史扩展的搜索和替换语法感兴趣:
$ echo foo
$ !:s/foo/bar
或者r
内置
$ echo foo
$ r foo=bar
但是,对于问题中的示例来说,这是不可能的,因为 foo 不是唯一的。