Skip to content
0

在 macOS 终端中集成 Himalaya、Khard 与 FZF,打造高效邮件工作流

博主当前使用 Outlook 管理邮件。在习惯了使用 Vim/Neovim 支持“多模态”的文本编辑器之后,再使用其他任何仅支持“插入”状态的文本编辑器,都不免会觉得低效。

有曾设想过把 Vim 搬进 Outloook 但没有找到优良的实现。于是反其道而行之,既然无法把多模态编辑器搬进邮件客户端,那么干脆在终端寻找和配置一个新的邮件客户端算了,当然,最好可以使用外置的 Vim/Neovim 文本编辑器。

对于许多开发者和终端爱好者来说,尽可能地停留在命令行界面中完成工作是一种追求,这不仅关乎效率,也关乎一种专注和沉浸的体验。邮件处理是日常工作中不可或缺的一环,而通过终端管理邮件,可以极大地减少上下文切换,让我们更专注于核心任务。

本文将详细介绍如何在 macOS 环境下,将终端邮件客户端 himalaya、联系人管理工具 khard 和模糊搜索神器 fzf 完美结合,并借助 grepawk 这两个强大的文本处理工具,打造一个完全由键盘驱动、高效流畅的邮件发送工作流。

准备工作

在开始之前,请确保你已经安装了 Homebrew,这是 macOS 上最方便的包管理工具。我们将使用它来安装所有必要的软件。

sh
# 如果尚未安装 Homebrew,请先执行此命令
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装所有核心工具
brew install himalaya khard fzf grep awk

Part 1: 核心工具解析

在我们将所有工具整合起来之前,首先需要理解每个工具的独立功能和基本用法。

1. himalaya - 现代化的终端邮件客户端

himalaya 是一个用 Rust 编写的命令行邮件客户端,它的设计目标是快速、安全且易于配置。它支持 IMAP 和 SMTP 协议,可以连接到几乎所有的邮件服务商(如 Gmail, Outlook, ProtonMail 等)。

基本配置

首次运行 himalaya 命令,向导会自动生成一个配置文件,位于 ~/Library/Application Support/himalaya/config.toml 这个路径下。为中心化管理配置文件,建议将其转移至 ~/.config/himalaya/config.toml 这个位置。无需任何额外设置,位于这两个位置的配置文件都可以被自动读取到。

toml
[accounts.Twine]
default = true
email = "alowree@twineintl.com"
display-name = "Alowree Xu - Twine"
downloads-dir = "/Users/alowree/Downloads"
signature = "Best regards, Alowree\n---\nwww.soundfreaq.com\nSoundfreaq® is a registered trademark of Twine.\nTwine Company Limited\nUnit 1506, Workingport Comm. Bldg., 3 Hau Fook Street, Tsim Sha Tsui, Kowloon, Hong Kong"
signature-delim = "\n"

backend.type = "imap"
backend.host = "twineintlcom.securemail.hk"
backend.port = 993
backend.login = "alowree@twineintl.com"
backend.encryption.type = "tls"
backend.auth.type = "password"
backend.auth.cmd = "security find-generic-password -w -a alowree@twineintl.com -s himalaya-imap"
message.send.backend.type = "smtp"
message.send.backend.host = "ud.1025.hk"
message.send.backend.port = 465
message.send.backend.login = "alowree@twineintl.com"
message.send.backend.encryption.type = "tls"
message.send.backend.auth.type = "password"
message.send.backend.auth.cmd = "security find-generic-password -w -a alowree@twineintl.com -s himalaya-smtp"

基本用法:

  • 列出邮件:

    sh
    # 列出收件箱(INBOX)中的最新 10 封邮件
    himalaya envelope list
  • 阅读邮件:

    sh
    # 读取指定 ID 的邮件(ID 可以从 list 命令中获取)
    himalaya message read <EMAIL_ID>
  • 撰写邮件:

    sh
    # 打开默认编辑器(如 Vim 或 Neovim)撰写新邮件
    himalaya message write

    根据配置文件,终端会使用 Neovim 打开一个邮件模板,格式如下:

    md
    From: Alowree Xu - Twine <alowree@twineintl.com>
    To:
    Subject:
    
    
    
    
    Best regards, Alowree
    ---
    www.soundfreaq.com
    Soundfreaq® is a registered trademark of Twine.
    Twine Company Limited
    Unit 1506, Workingport Comm. Bldg., 3 Hau Fook Street, Tsim Sha Tsui, Kowloon, Hong Kong

    用例: 假设你需要快速回复一封工作邮件。你可以通过 himalaya envelope list 找到邮件 ID,然后用 himalaya message reply <EMAIL_ID> 直接在终端中完成回复,整个过程无需打开任何图形界面的邮件 App。

2. khard - 你的命令行通讯录

khard 是一个用于创建、编辑和搜索 CardDAV 联系人的命令行工具。这意味着它可以与你的 iCloud、Google Contacts 或其他支持 CardDAV 的服务同步联系人。

基本配置:

创建 ~/.config/khard/khard.conf 配置文件如下:

[addressbooks]
[[contacts]]
path = ~/.contacts

[general]
editor = nvim
merge_editor = vimdiff

[contact]
show_all_custom_fields = true
show_custom_fields = true
show_hidden_fields = true
display = first_name
group_by_addressbook = false
reverse_order = false
search_in_source_files = true
sort = last_name
show_uids = false
vcard_extension = .vcf
preferred_vcard_version = 3.0

在上面配置中,我们指定了联系人卡片的保存位置,并且在增添、编辑联系人时,指定了使用 Neovim 作为文本编辑器。

基本用法:

  • 添加新的联系人:

    sh
    khard add

    Neovim 此时会打开一个联系人模板,你可以选择输入、保存,或者不保存退出而自动丢弃。

  • 列出所有联系人:

    sh
    khard list

    输出通常是 姓名 <邮箱地址> 的格式。

  • 显示联系人详情:

    sh
    # 使用姓名或 UID 进行搜索
    khard show "张三"
  • 查找联系人邮箱:

    sh
    khard email "张三"

真实世界用例: 你需要给同事“李四”发送邮件,但忘记了他的完整邮箱地址。只需在终端输入 khard email "李四",就能立刻获得他的邮箱地址,可以直接复制粘贴使用。

3. fzf - 无处不在的模糊搜索神器

fzf (fuzzy finder) 是一个通用的命令行模糊搜索工具。它从标准输入(stdin)读取文本列表,然后提供一个交互式的界面让用户进行筛选,最后将被选中的那一行输出到标准输出(stdout)。

基本用法:

  • 在历史命令中搜索:

    sh
    history | fzf

    这会弹出一个可交互的窗口,你可以输入任何相关的词语来快速找到并执行之前用过的命令。

  • 切换 Git 分支:

    sh
    git branch | fzf | xargs git checkout

真实世界用例:fzf 的强大之处在于它可以与任何产生列表输出的命令结合。当你面对成百上千行日志、文件列表或历史记录时,fzf 可以让你在几秒钟内定位到你需要的那一项。

4. grepawk - 文本处理的双雄

这两个是 Unix/Linux 世界的经典工具,用于处理文本流,是自动化脚本的基石。

  • grep (Global Regular Expression Print):grep 用于在文本中搜索匹配特定模式(正则表达式)的行。

    真实世界用例: 你想在邮件列表中快速找到所有来自 boss@company.com 的邮件。

    sh
    himalaya list | grep "boss@company.com"
  • awk:awk 是一个强大的文本扫描和处理语言。它逐行读取输入,并能根据指定的分隔符将每一行分割成多个字段($1, $2, ...),然后对这些字段进行操作。

    真实世界用例: 假设 khard list 的输出是 张三 <zhangsan@example.com>。我们只想要尖括号里的邮箱地址。

    sh
    echo "张三 <zhangsan@example.com>" | awk -F'[<>]' '{print $2}'

    这里,-F'[<>]' 设置了分隔符为 <>awk 会将这行分割成三个字段:"张三 ""zhangsan@example.com"""{print $2} 则打印出第二个字段,也就是我们想要的邮箱地址。

Part 2: 终极整合:compose_with_khard

了解了每个工具的功能后,我们现在的目标是:在执行 himalaya compose 命令时,不再手动输入收件人的邮箱地址,而是从 khard 的联系人列表中通过 fzf 模糊搜索并自动填充。

我们将通过在 ~/.zshrc (或 ~/.bashrc) 文件中定义一个 shell 函数来实现这个工作流。

1. 用户工作流(从用户视角)

整合完成后,你的邮件发送流程将变成这样:

  1. 在终端中输入 compose_with_khard 并按回车。
  2. 屏幕会提示你选择“To”收件人,并弹出一个 fzf 搜索框。你可以用 TAB 键选择多个联系人。
  3. 选择完毕后按回车,屏幕会再次提示你选择“CC”收件人(如果不需要可以直接按 ESC 取消)。
  4. 最后,终端会提示你输入邮件主题。
  5. 输入主题并按回车后,你配置的默认文本编辑器会自动打开,邮件的收件人、抄送和主题都已经为你填好了。

整个过程行云流水,双手无需离开键盘。

2. 代码深度解析

请将以下函数添加到你的 ~/.zshrc 文件末尾,然后执行 source ~/.zshrc 使其生效。

sh
# ~/.zshrc

select_khard_emails () {
  khard email --parsable | \
  grep -v "searching for 'ALL'..." | \
  fzf --multi --preview 'khard show {3}' --preview-window=right:60% \
      --header "Select recipients (Tab to select multiple)" | \
  awk '{print $1}'
}

compose_with_khard() {
  echo "Selecting 'To' recipients..."
  local to_emails
  to_emails=$(select_khard_emails)
  local to_headers=()
  if [ -n "$to_emails" ]; then
    # Join the emails with a comma for a single 'To:' header
    local to_list
    to_list=$(echo "$to_emails" | paste -sd, -)
    to_headers=(--header "To:$to_list")
  fi

  echo "Selecting 'CC' recipients (optional)..."
  local cc_emails
  cc_emails=$(select_khard_emails)
  local cc_headers=()
  if [ -n "$cc_emails" ]; then
    # Join the emails with a comma for a single 'Cc:' header
    local cc_list
    cc_list=$(echo "$cc_emails" | paste -sd, -)
    cc_headers=(--header "Cc:$cc_list")
  fi

  echo "Enter Subject: "
  read SUBJECT

  himalaya message write \
    "${to_headers[@]}" \
    "${cc_headers[@]}" \
    --header "Subject:$SUBJECT"
}

这段代码定义了两个函数 select_khard_emailscompose_with_khard,它们协同工作,提供了一个非常强大的邮件撰写流程,包括选择收件人(To)、抄送人(CC),以及手动输入主题。

让我们先分解辅助函数 select_khard_emails

  • select_khard_emails () { ... }: 定义了一个可重复使用的函数,其唯一目的是从 khard 中选择并返回一个或多个邮箱地址。

  • khard email --parsable:

    • khard email 命令用于提取联系人的邮箱地址。
    • --parsable 参数使其输出格式变为机器友好的 email@domain.com 姓名 UID,非常适合后续的管道处理。
  • | grep -v "searching for 'ALL'...": khard 在执行时可能会输出一行状态信息,如 searching for 'ALL'...grep -v 的作用是反向匹配,即过滤掉包含该文本的行,确保只有纯净的联系人数据进入下一步。

  • | fzf --multi --preview '...' --header "...": 这是核心的交互界面。

    • --multi: 允许使用 TAB 键选择多个联系人。
    • --preview 'khard show {3}': 这是 fzf 的一个高级功能。它会为当前高亮的行显示一个预览窗口。{3} 代表 khard 输出的第三列,即联系人的 UID。因此,fzf 会实时执行 khard show <UID>,在预览窗口中显示该联系人的详细信息,非常方便。
    • --header "...": 在 fzf 窗口顶部显示帮助信息。
  • | awk '{print $1}': fzf 将选中的所有行(例如 email@domain.com 姓名 UID)输出给 awkawk '{print $1}' 的作用是只打印每一行的第一个字段,也就是我们最终需要的邮箱地址。


接下来,我们分析主函数 compose_with_khard

  • compose_with_khard() { ... }: 定义了主函数,也就是用户直接调用的函数。

  • echo "Selecting 'To' recipients...": 向用户显示清晰的提示,告知当前正在选择“收件人”。

  • local to_emails; to_emails=$(select_khard_emails): 调用我们上面定义的辅助函数来选择收件人,并将返回的、由换行符分隔的邮箱地址列表存入 to_emails 变量。

  • if [ -n "$to_emails" ]; then ... fi: 检查用户是否至少选择了一个收件人。

    • local to_list; to_list=$(echo "$to_emails" | paste -sd, -): 如果选择了,paste -sd, - 命令会将 to_emails 中由换行符分隔的多行邮箱地址合并成一个由逗号 , 分隔的单行字符串。
    • to_headers=(--header "To:$to_list"): 创建一个数组 to_headers,其中包含一个 himalaya 能识别的参数,例如 --header "To:a@x.com,b@y.com"
  • echo "Selecting 'CC' recipients (optional)...": 以完全相同的逻辑,提示并允许用户为“抄送”选择联系人,并将结果处理成 cc_headers 数组。

  • echo "Enter Subject: ": 提示用户输入邮件主题,并通过 read 命令将其存入 SUBJECT 变量。

  • himalaya message write ...: 最后,调用 himalaya 的撰写命令。

    • "${to_headers[@]}""${cc_headers[@]}": 这种 ${array[@]} 语法可以安全地将数组中的元素作为独立的参数传递给命令。如果用户没有选择收件人或抄送人,对应的数组为空,这里就不会传递任何参数,非常灵活。
    • --header "Subject:$SUBJECT": 传递邮件主题。

执行此命令后,himalaya 会打开你的默认文本编辑器,邮件的 To, Cc, 和 Subject 头部字段都已经为你预先填好了。

总结与资源

通过将 himalaya, khard, fzf, grep, awk, 和 paste 等多个强大的单一功能工具组合成 shell 函数,我们创造了一个功能完整、交互友好且极为高效的邮件撰写工作流。这完美地展示了 Unix 哲学的精髓所在:编写只做一件事并把它做好的程序,然后将它们组合起来以完成复杂的任务。

希望这篇教程能帮助你更好地在终端中管理你的数字生活。

待解问题

  • 添加附件 一直报错...
  • 回复邮件
  • 转发邮件
  • 本地保存邮件
  • 多帐号支持

拓展阅读

  • Himalaya:
  • Khard:
  • FZF:
    • GitHub 仓库 (尤其推荐查看其 man 手册页,了解 --preview 等高级用法)
  • Awk, Grep, Paste:
    • 这些都是经典的 Unix 工具,网络上有海量的教程。建议通过 man awk, man grep, man paste 来阅读它们的官方手册页,这是最权威的学习资料。
最近更新