是否有与 Ctrl-R 等效的命令,但针对的是单个参数,而不是整个命令(zsh)?

是否有与 Ctrl-R 等效的命令,但针对的是单个参数,而不是整个命令(zsh)?

CtrlR经常使用它来搜索我运行过的先前命令,然后编辑它们。我还经常使用它Alt.来循环显示先前命令的最后一个参数。我希望有一个可以同时完成这两件事的东西。

有没有办法搜索前面命令的各个参数并将其插入到当前命令中?例如:

$ run_pipeline --arg1 blah --arg2 blah some/long/path some/other/long/path
... run many other commands ...
$ ls -l <a keystroke that will let me bring up some/long/path>

理想情况下,我希望它是交互式的,例如CtrlR/ CtrlS,这样我可以在插入参数之前预览我将要插入的内容并编辑我的搜索。

答案1

使用以下 zle 小部件,您应该获得靠近你想要的行为:

fzf-last-word-widget() {
  local POSITION RECALL_ARGUMENT
  if [[ ! -z $NUMERIC ]]; then
    POSITION=-${NUMERIC}
  else
    POSITION=-1
  fi

  histlist=$(
    for histitem ("${(@f)$(fc -l 1)}") {
      histwords=(${(z)histitem})
      print -- $histwords[$POSITION]
    }
  )
  RECALL_ARGUMENT=$(print $histlist | fzf --tac +s -e)
  if [[ ! -z $RECALL_ARGUMENT ]]; then
    LBUFFER="${LBUFFER}${(q)RECALL_ARGUMENT}"
    zle redisplay
  fi
}
zle -N        fzf-last-word-widget
bindkey '^Xr' fzf-last-word-widget
  • 我用fzf呈现历史记录最后参数的漂亮交互列表。因此需要安装它。
  • 该小部件尊重数字参数还可以选择倒数第二个、倒数第三个等参数。请man zshzle检查数字参数如何在命令行中输入此内容。
  • 在示例中,小部件绑定到CTRL-X R

现在,为了更好地解释该小部件的工作原理,让我们将其拆开:

  • 是个zsh 行编辑器它负责你在提示时的交互体验(相当于阅读行狂欢)。

  • zle 小部件只是一个特殊的 shell 函数,它通过引入 zlezle -N fzf-last-word-widget然后可以像任何内置小部件一样绑定到键序列(bindkey '^Xr' fzf-last-word-widget)。

  • 你可以通过数字参数到小部件;引用自man zshzle

    默认情况下,可以在 emacs 模式下按住 alt 键并输入数字,或在每个数字前按 Esc 键来输入;在 vi 命令模式下,可以在输入命令前输入数字。

该数字参数以变量形式传递给小部件$NUMERIC并可在其中使用。

  • 这是第一个代码块要处理的内容,它通过检查是否$NUMERIC不等于空字符串 ( ! -z $NUMERIC) 来检查是否传递了数字参数。如果存在,则将其逆存储在local POSITION名为 的局部变量 ( )中$POSITION,否则$POSITION默认为 -1。

  • zle 中有许多特殊变量,其中一些可以更改;请参阅用户定义的小部件man zshzle。这里相关的是保存$LBUFFER您输入的命令行的部分左边到光标(因此L名称中为;还有$RBUFFER)。如果您编辑它,光标会相应地移动,就在完整缓冲区(还有$BUFFER)被重新绘制之后zle redisplay。在这里,我将(本地)字符串附加$RECALL_ARGUMENT$LBUFFER,但让小心使用引用(q) 扩展标志(参见man zshexpn),因此像 这样的参数path with spaces/foobar最终会变成。 (如果为空,path\ with\ spaces/foobar则会进行检查,例如当您中止 时。否则,您的命令行上最终会出现两个单引号。)$RECALL_ARGUMENTfzf''

  • 现在任务就简化为根据您的需要构建 $RECALL_ARGUMENT。如前所述,我使用了fzf呈现一个漂亮的交互式列表,您可以轻松搜索并选择所需的内容fzf标准输入并把你的选择写给标准输出。参数禁用列表排序(+s),反转列表(--tac,我个人更喜欢)并启用精确匹配(-e)。为了使其更具可读性,我拒绝编写一行代码,因此fzf在数组中单独构建输入$histlist。因此我们通过管道传递$histlist给并通过命令替换fzf将输出读入。$RECALL_ARGUMENT$()

  • 我们需要历史记录,因此我们从第一行开始通过 获取它fc -l 1,在换行符上将其拆分为数组(扩展标志(@f)),然后使用 循环遍历每一行for。我们确实需要用"${(@f)$(fc -l 1)}"引号括起来,以防止在空格处拆分。

  • 现在$histline包含历史记录中的一行,例如1 echo foo。我们将这一行拆分成壳字通过(z)扩展标志。外壳词意味着echo foo\ bar只拆分成echo&foo\ bar而不拆分成echofoo\&bar并将结果存储到 中$histwords;这应该是一个数组,这就是为什么值用括号括起来的原因(...)

  • 拆分后,我们挑选出想要的外壳词,例如$POSITION一(因为$POSITION是负数,所以从后面开始计算),然后打印出来。(如果您想要单独选择所有参数,您可以使用命令,printf '%s\n' "${histwords[@]:1}"或者使用print -l -- $histwords[2,-1]应该相等的命令。)

  • 所有打印的字符串都聚集在数组中,然后按照上面的说明$histlist进行传递。fzf

相关内容