如何在 zsh 中替换前一个命令的特定参数?

如何在 zsh 中替换前一个命令的特定参数?

假设我有以下命令:

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

现在我想用 替换foo后面4bar,因此它变成:

/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 不是唯一的。

相关内容