﻿---
title: "我从 fzf 切换到 Television：用声明式 Channel 实现跨 Shell 模糊查找"
date: 2025-07-24
excerpt: 一个跨 Shell 的模糊查找器，用智能快捷触发替代手动路径跳转。本文会解释它的工作原理、配置方法，以及它为什么可能替换你当前的方案。
tags:
  - Terminal
  - Shell
  - TUI
  - Workflow
  - Productivity
cover: https://assets.vluv.space/cover/ToolChain/television.webp
lang: zh-CN
i18n:
  en: /en/television
  translation: 2
updated: 2026-05-09 14:18:21
---

<script data-swup-reload-script type="module" src="/js/components/tab.js"></script>

你可能遇到过这些场景：

- **路径跳转循环：** 反复输入 `{sh} cd foo/ && ls`，结果发现进错目录，再 `{sh} cd ../..` 重来一遍。
- **历史命令捞针：** 一直按 <kbd>Up</kbd>，只为找昨天执行过的一条复杂命令。
- **PID 手动接力：** 先 `{sh} ps aux | grep python` 找进程，再复制 PID，最后手动输入 `{sh} kill`。

像 **fzf** 这样的模糊查找工具可以缓解这些问题，但我后来切到了 Television，因为它在 UI/UX 和跨 Shell 一致性上更合适我的工作流。

这篇文章写给希望更快完成终端导航、但又不想维护一堆 Shell 特定胶水代码的用户。读完你会知道模糊查找器的基本原理、`tv` 的配置方式，以及它是否比你当前方案更匹配你的习惯。

## 模糊查找器是什么

假设你要在项目里找某个子目录，可以先用 `{sh} fd --type directory` 列出目录：

```shell
$ fd --type directory
src/
...
packages/core/utils/src/helpers
packages/core/utils/src/parsers
packages/ui/components/Button
```

当列表很长时，人工扫一遍很痛苦。你可以用 `{sh} fd --type directory | rg <src>` 先过滤，但它依然是静态结果。模糊查找器真正有价值的地方在于交互能力：

```sh
fd --type directory | fzf --preview 'eza --all --git --long --no-time --color=always --icons {}'
```

把结果 pipe 给 `{sh} fzf` 后，你会得到一个**可交互过滤 + 侧边预览**的界面：

![television_fzf_example.webp](https://assets.vluv.space/television_fzf_example.webp)

### 底层发生了什么

<script data-swup-reload-script type="module" src="/js/components/accordion.js"></script>

<x-accordion>
  <accordion-item title="异步流（Feeder）">

执行这条 pipeline 后，`fd` 会立刻开始扫描。关键点在于：`fzf` 不会阻塞等待 `fd` 全部结束。

它使用的是**流式架构**：`fd` 只要向 *stdout* 吐出一条路径，`fzf` 就会从 *stdin* 立刻读入并更新列表。因此即使后台还在扫几千个文件，你也能几乎瞬间看到可操作的 UI。

  </accordion-item>
  <accordion-item title="预览引擎（Runner）">

`--preview` 让 `fzf` 从“文本筛选器”升级为“命令调度器”。

  - **动态替换** `{}` 是关键占位符。你移动光标时，`fzf` 会把 `{}` 动态替换成当前高亮路径（比如 `packages/ui`）。
  - **子进程执行** 每次光标移动，`fzf` 会启动一个临时 shell 进程执行拼好的命令（如 `eza ... packages/ui`）。
  - **ANSI 透传** 因为我们显式设置了 `--color=always`，`eza` 会输出原始 ANSI 转义序列。`fzf` 在这里相当于一个迷你终端渲染器：捕获这些序列并在侧栏正确还原颜色与图标。

  </accordion-item>
  <accordion-item title="性能优化（Debouncer）">

你可能会问：“我快速滚动时，是否会一秒触发 50 次 `eza`？”

`fzf` 做了智能 **debounce**。当你快速移动光标时，它会延迟触发预览，等光标稳定一小段时间再执行；同时会主动杀掉已经过期的 `eza` 进程，避免 CPU 被无效预览拖垮。

  </accordion-item>
  <accordion-item title="最终输出（Composer）">

当你选中目标目录并按下 **Enter**，`fzf` 退出、关闭 `eza` 预览，并把选中路径输出到标准输出。

这正是 Unix 组合哲学的核心。因为 `fzf` 输出的是干净文本到 **stdout**，你可以继续和其他命令拼接，比如直接跳转：

`{sh} cd $(fd --type directory | fzf ...)`

  </accordion-item>
</x-accordion>

### 与 Shell 集成

模糊查找器的真正威力在于嵌入 Shell 工作流。你在命令中途按 <kbd>Ctrl + T</kbd>，选中文件/目录后结果会直接插入光标位置；不用手打路径，也不用复制粘贴。

如果你不是 bash、zsh、fish（这三者有官方支持），通常需要写 Shell 特定胶水代码。下面是 Nushell 里结合 `{nu} commandline edit` 的配置示例：

```nushell
{
    name: fuzzy_file
    modifier: control
    keycode: char_t
    mode: [emacs, vi_normal, vi_insert]
    event: {
        send: executehostcommand
        cmd: "commandline edit --insert (fd --type file | fzf --preview 'eza --all --git --long --no-time --color=always --icons {}')"
    }
}
```

## 从 fzf 到 Television

到这里你已经看到了 fzf 的机制。它很强，但一旦你跨不同 Shell 使用，就需要维护不同集成脚本。我自己主要在 Windows 与 macOS 上使用 nushell，但偶尔也会用 zsh、bash。继续用 fzf 的话，配置维护成本会持续增长。

Television（`tv`）的思路是：把复杂性从**脚本层**搬到**配置层**。它是一个快速、可移植的模糊查找器，尽可能保证多 Shell 下一致体验：

- **声明式 Channel：** 不再依赖复杂 pipe，把逻辑写进 **TOML Channel**，更易读、易分享、易版本化。
- **Shell Integration：** 在 `config.toml` 配一次，即可在 bash、zsh、fish、nushell 中获得一致行为（例如 <kbd>Ctrl + T</kbd> 智能补全、<kbd>Ctrl + R</kbd> 历史搜索），无需手工逐个绑定。
- **现代化 UX 与可发现性：** 它有比较精致的 UI，以及动态**快捷键提示栏**。这对上手非常友好，你可以边用边学，不需要先死记命令。

<video autoplay loop muted playsinline>
  <source src="https://assets.vluv.space/television_1.mp4" type="video/mp4">
</video>

### 快速开始

#### 第一步：安装 Television

为你的系统安装 `tv` 二进制：

```shell
# Windows (Scoop)
scoop install television

# macOS (Homebrew)
brew install television

# Linux / Other
cargo install television
```

#### 第二步：下载 Channel

运行 `{sh} tv update-channels` 下载社区维护的 channel。它们是预定义搜索源，告诉 `tv`“搜什么、怎么展示”。

#### 第三步：Shell 集成

把 `tv` 初始化脚本注入你的 Shell 配置。它会注册两个全局快捷键，把 `tv` 直接接入日常命令流：

```sh
# Zsh
echo 'eval "$(tv init zsh)"' >>~/.zshrc

# Bash
echo 'eval "$(tv init bash)"' >>~/.bashrc

# Fish
tv init fish | source # Add to config.fish
```

## Channel

> Channels are short configuration recipes that typically dictate what tv should search through and what's displayed on the screen along with various other options.
>
> [Channels | Television](https://alexpasmantier.github.io/television/docs/Users/channels)

Channel 决定了 `tv` 搜索范围和结果展示方式，文件位置在 `~/.config/television/cable/`。

你之前执行的 `{sh} tv update-channels` 会把常用 channel 从社区拉取到本地。

```sh
$ tree ~/.config/television
~/.config/television
├── config.toml
├── cable
│   ├── alias.toml
│   ├── aws-buckets.toml
│   ├── aws-instances.toml
│   ├── git-repos.toml
│   ├── nu-history.toml
│   ├── text.toml
│   └── zsh-history.toml
```

> [!WARNING]- 依赖要求
>
> - 某些 channel 依赖额外 CLI 工具，例如 `{sh} bat` 和 `{sh} fd`。
> - 如果你使用 nushell，建议在 `config.toml` 里设定 `shell = bash/zsh`[^1]，因为很多社区 channel 使用的是 POSIX 兼容 shell 语法。
>
> ```toml config.toml
> shell = "bash" # 也可以用 zsh
> ```

### 用 Channel 增强工作流

这项能力，也就是**智能 Shell 集成**，是我从 fzf 切到 television 的主要原因。

不同于静态文件搜索，按下 <kbd>Ctrl + T</kbd> 时，`tv` 会根据你当前命令缓冲区内容自动触发对应 channel，实现上下文感知：

- **编辑场景：** 输入 `{sh} vim` 或 `{sh} cat` 时触发 `files` channel。
- **导航场景：** 输入 `{sh} cd` 时触发 `dirs` channel。
- **进程管理：** 输入 `{sh} kill` 或 `{sh} ps` 时触发 `procs` channel。

```toml config.toml
[shell_integration.channel_triggers]
"dirs" = ["cd"]
"files" = ["cat", "vim"]
```

<x-tabs>

<x-tab title="Files" active>

<video autoplay loop muted playsinline>
  <source src="https://assets.vluv.space/television_1.mp4" type="video/mp4">
</video>

</x-tab>

<x-tab title="Directories">

<video autoplay loop muted playsinline>
  <source src="https://assets.vluv.space/television_2.mp4" type="video/mp4">
</video>

</x-tab>

</x-tabs>

这种行为由 `config.toml` 中的 `{toml} [shell_integration.channel_triggers]` 声明。你可以按自己的工作流自定义映射（完整示例可参考我 GitHub 上的 [config.toml](https://www.google.com/url?sa=E&q=https%3A%2F%2Fgithub.com%2FEfterklang%2Fdotfiles%2Fblob%2Fmain%2Ftui_cli%2Ftelevision%2Fconfig.toml)）。

## 需要权衡的点

Television 不是所有模糊查找场景下的完美替代。

**适合选 `tv` 的情况：**
- 你经常切换 Shell，想要一致行为
- 你偏好配置文件而不是脚本
- 你看重内置 channel 系统对常见搜索任务的覆盖

**继续用 fzf 更合理的情况：**
- 你已经有大量高度定制的 fzf 集成，迁移成本过高
- 你对当前方案很满意，也不需要跨 Shell 可移植性

真正值得注意的是：Shell 工具碎片化会产生隐性维护成本。你今天为 bash 定制的一段 fzf 配置，未来切到别的 Shell 时很可能变成迁移负担。`tv` 试图在配置层解决这个问题，而不是在脚本层反复补丁。

## 延伸阅读

如果想深入了解完整能力，建议直接阅读 [Television 文档](https://www.google.com/url?sa=E&q=https%3A%2F%2Falexpasmantier.github.io%2Ftelevision%2F)。另外也推荐看 [Unix Community Channels](https://www.google.com/url?sa=E&q=https%3A%2F%2Falexpasmantier.github.io%2Ftelevision%2Fcommunity%2Fchannels-unix)，可以快速浏览社区最新集成玩法。

顺带一提，这套文档光从视觉上也值得一看：界面干净，配合像素风 Doto 字体，风格非常鲜明。

[^1]: [Many channel don't work in nushell · Issue #884 · alexpasmantier/television](https://github.com/alexpasmantier/television/issues/884)
