菜单

动态重载与外部命令

相关源文件

本文档介绍了 fzf 动态重载输入数据和与外部命令集成的功能。这些功能使用户能够创建交互式工作流,其中项目列表可以按需或自动刷新,并且 fzf 可以根据用户的选择执行外部命令。

概述

fzf 提供了多种机制与外部命令交互并动态更新其输入

  1. 重载操作 (Reload Action):允许通过执行外部命令来刷新输入列表
  2. 执行操作 (Execute Actions):运行带有选定项目的外部命令
  3. 转换操作 (Become Action):将 fzf 转换为另一个进程
  4. 预览命令 (Preview Commands):在预览窗口中显示命令输出

这些功能使得 fzf 能够成为其他命令行工具的交互式界面,从而实现强大的工作流。

来源: ADVANCED.md162-164 src/actiontype_string.go123-136

动态重载 (Dynamic Reloading)

reload 操作允许 fzf 通过执行任意命令来动态更新其候选列表。这使得创建交互式界面成为可能,这些界面可以在不重启 fzf 的情况下刷新其数据。

基本重载用法 (Basic Reload Usage)

reload 操作可以使用 --bind 选项绑定到按键或事件。触发时,fzf 将执行指定的命令,并将其输出作为新的候选列表。

示例

fzf --bind 'ctrl-r:reload(ls)'

这会将 Ctrl-R 绑定到使用 ls 命令的输出来重载列表。

来源: ADVANCED.md171-183

将重载绑定到事件 (Binding Reload to Events)

除了按键绑定之外,reload 还可以绑定到 fzf 事件,例如 startchange

  1. 启动事件 (Start Event):fzf 启动时执行命令

    fzf --bind 'start:reload(ls)'
    
  2. 更改事件 (Change Event):查询字符串更改时执行命令

    fzf --bind 'change:reload(find . -name "{q}*")'
    

命令中的 {q} 占位符会被当前查询字符串替换,从而允许基于用户输入进行动态过滤。

来源: ADVANCED.md346-356 ADVANCED.md402-405

限制重载操作 (Debouncing Reload Actions)

当将 reload 绑定到 change 事件时,通常需要添加一个小的延迟来减少键入时的命令执行次数。

fzf --bind 'change:reload:sleep 0.1; find . -name "{q}*" || true'

sleep 0.1 在执行命令前添加 100 毫秒的延迟,而 || true 确保即使命令失败 fzf 也会继续执行。

来源: ADVANCED.md380-383

外部命令集成 (External Command Integration)

fzf 提供了多个用于集成外部命令的操作,允许用户根据其选择执行命令。

执行操作 (Execute Actions)

fzf 提供了三种用于执行外部命令的操作:

  1. execute:执行命令并显示其输出
  2. execute-silent:执行命令而不显示其输出
  3. execute-multi:为每个选定的项目执行命令

这些操作可以使用 --bind 选项绑定到按键。

fzf --bind 'enter:execute(echo {})'

在命令字符串中,可以使用以下占位符:

  • {}:当前选定的项目
  • {+}:所有选定的项目,空格分隔
  • {q}:当前查询字符串

来源: ADVANCED.md572-577 src/actiontype_string.go117-119

转换操作 (The Become Action)

become 操作(fzf 0.38.0 中引入)允许 fzf 将自身转换为另一个进程。这对于创建无缝工作流非常有用,其中 fzf 用于选择,然后将其交接给另一个程序。

fzf --bind 'enter:become(vim {})'

当触发 become 操作时,fzf 将以一个特殊退出码退出,并由指定的命令代替它执行,该命令继承 fzf 的终端会话。

内部而言,become 操作的工作方式如下:

  1. 将命令写入一个带有 .become 后缀的临时文件。
  2. 以退出码 130(定义为 ExitBecome)退出。
  3. 父进程检测到此退出码,从临时文件中读取命令,并执行它。

来源: src/proxy.go20-161 ADVANCED.md338-341

实际应用

更新进程列表 (Updating Process Lists)

一个常见的用例是创建一个交互式的进程查看器,该查看器可以按需刷新。

(date; ps -ef) |
  fzf --bind='ctrl-r:reload(date; ps -ef)' \
      --header='Press CTRL-R to reload' --header-lines=2 \
      --preview='echo {}' --preview-window=down,3,wrap

这会显示一个进程列表,可以通过按 Ctrl-R 来刷新。

来源: ADVANCED.md176-182

切换数据源 (Toggling Between Data Sources)

您可以设置多个重载绑定来在不同数据源之间切换。

find * | fzf --prompt 'All> ' \
           --header 'CTRL-D: Directories / CTRL-F: Files' \
           --bind 'ctrl-d:change-prompt(Directories> )+reload(find * -type d)' \
           --bind 'ctrl-f:change-prompt(Files> )+reload(find * -type f)'

这允许在列出所有文件、仅列出目录或仅列出常规文件之间进行切换。

来源: ADVANCED.md202-207

交互式 Ripgrep 集成 (Interactive Ripgrep Integration)

一个强大的应用是使用 fzf 作为 Ripgrep 的交互式接口。

RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
fzf --ansi --disabled --query "$INITIAL_QUERY" \
    --bind "start:reload:$RG_PREFIX {q}" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --bind 'enter:become(vim {1} +{2})'

这创建了一个交互式 grep 界面,其中:

  1. 结果随您键入而更新。
  2. 预览窗口显示匹配文件,并高亮显示匹配行。
  3. 按 Enter 键会在匹配行在 Vim 中打开选定的文件。

来源: ADVANCED.md357-371 ADVANCED.md400-412

预览窗口集成 (Preview Window Integration)

预览窗口可以显示外部命令的输出,为选定的项目提供上下文。

基本预览命令 (Basic Preview Commands)

--preview 选项指定一个为每个选定项目运行的命令。

fzf --preview 'cat {}'

这会在预览窗口中显示选定文件的内容。

实时预览更新 (Live Preview Updates)

预览窗口可以使用 follow 标志显示长时间运行命令的实时更新。

fzf --preview-window follow --preview 'tail -f {}'

这对于监控日志文件或其他持续更新的内容特别有用。

来源: ADVANCED.md547-558 ADVANCED.md575-576

Kubernetes 示例 (Kubernetes Example)

一个实用的例子是创建一个交互式 Kubernetes Pod 查看器。

fzf --info=inline --layout=reverse --header-lines=1 \
    --prompt "$(kubectl config current-context)> " \
    --header 'Enter (kubectl exec) / CTRL-O (open log in editor) / CTRL-R (reload)' \
    --bind 'start,ctrl-r:reload:kubectl get pods --all-namespaces' \
    --bind 'enter:execute:kubectl exec -it --namespace {1} {2} -- bash' \
    --bind 'ctrl-o:execute:${EDITOR:-vim} <(kubectl logs --all-containers --namespace {1} {2})' \
    --preview-window up:follow \
    --preview 'kubectl logs --follow --all-containers --tail=10000 --namespace {1} {2}'

这创建了一个交互式界面,用于:

  1. 查看 Kubernetes Pod。
  2. 进入 Pod 执行 shell 命令。
  3. 实时查看 Pod 日志。
  4. 在编辑器中打开日志。

来源: ADVANCED.md566-577

实现细节

事件驱动架构

fzf 使用事件驱动的架构来处理用户输入和触发重载、执行等操作。

来源: test/test_core.rb123-136 src/actiontype_string.go117-136

跨平台实现

fzf 的终端处理和外部命令执行实现了跨平台支持。

  1. Unix 系统:使用标准文件描述符和 POSIX API。
  2. Windows:使用 Windows Console API 和特殊的终端仿真处理。

实现包括特定于平台的代码,用于:

  • 打开 TTY 设备
  • 终端状态管理
  • 进程执行

来源: src/tui/light_unix.go1-134 src/tui/light_windows.go1-166 src/tui/ttyname_unix.go1-55 src/tui/ttyname_windows.go1-22

成为操作实现

become 操作通过特殊的退出码和临时文件来实现。

  1. fzf 将命令写入一个带有 .become 后缀的临时文件。
  2. fzf 以退出码 130 (ExitBecome) 退出。
  3. 父进程检测到此退出码。
  4. 父进程从临时文件中读取命令。
  5. 父进程执行该命令,替换自身。

这使得从 fzf 到另一个进程的无缝过渡成为可能。

来源: src/proxy.go130-155

最佳实践

高效的重新加载绑定

  1. 使用防抖配合 sleep 来绑定到 change 事件,以减少命令执行次数。
  2. 使用 || true 处理命令失败,以确保即使命令失败,fzf 也能继续运行。
  3. 使用适当的头行来跳过命令输出的头部信息。

组合操作

操作可以使用 + 操作符组合,以创建复杂行为。

fzf --bind 'ctrl-r:change-prompt(Ripgrep> )+reload(rg {q})'

这会更改提示符,并使用单个按键绑定重新加载列表。

状态管理

对于更复杂的工作流程,您可以使用临时文件来维护命令之间的状态。

fzf --bind 'ctrl-t:transform-query:echo {q} > /tmp/state; cat /tmp/other-state'

这允许复杂的状态转换和模式切换。

来源: ADVANCED.md460-463 ADVANCED.md486-502

结论

fzf 的动态重新加载和外部命令集成功能使其成为创建交互式命令行界面的强大工具。通过将这些功能与 fzf 的模糊查找能力相结合,用户可以构建复杂的、提高生产力的工作流程,并简化复杂的任务。

有关相关功能的更多信息,请参阅