Mac(四)自定义按键工具 Hammerspoon 的安装和使用
目录
- 一、安装 Hammerspoon
- 1. 官方安装(推荐)
- 2. 手动安装
- 3. 首次配置
- 4. 配置文件结构
- 5. 示例配置
- 3. 重载配置
- 二、实用代码片段
- 1. 重新加载配置
- 2. 快捷输入文字
- 3. 打开网页
- 5. 文字缩写填充
- 6. 窗口大小变化
- 7. 防沉迷提醒
- 8. 剪切板历史
- 9. 快速分屏(替代 Magnet)
- 10. 应用启动
- 11. 电量监控
- 12. 网络切换
- 三、调试与维护
- 1. 控制台调试
- 2. 常用命令
- 3. 错误排查
- 四、推荐插件及安装方式
- 1. 主流插件安装指南 🔌
- 第1步 SpoonInstall(插件管理器)
- 第2步 WinWin(窗口管理增强)
- 第3步 ClipboardTool(剪切板历史)
- 第4步 FadeLogo(状态栏动画)
- 2. 插件管理技巧
- 自动更新插件
- 插件冲突排查
- 3. 社区资源
- 五、扩展资源
- 1. 官方文档
- 2. 精品配置
- 3. 小编配置

- 官网地址: https://www.hammerspoon.org/
- 官方文档: https://www.hammerspoon.org/go/
- 官方下载: https://github.com/Hammerspoon/hammerspoon/releases/latest
- GitHub: https://github.com/Hammerspoon/hammerspoon
🚀 Hammerspoon 是 macOS 上最强大的自动化工具,通过 Lua 脚本实现:
- 全局快捷键自定义
- 窗口管理自动化
- 应用快速切换
- 系统状态监控
- 任意功能扩展
比 Karabiner 更轻量,比 BetterTouchTool 更自由!
一、安装 Hammerspoon
1. 官方安装(推荐)
brew install --cask hammerspoon
2. 手动安装
在 官网下载 之后,双击打开 → 拖拽到 Applications
文件夹
3. 首次配置
- 打开应用后,菜单栏会出现「🔨」图标
- 点击图标 → 「Open Config」打开配置文件目录
- 系统会要求授予「辅助功能」权限:
系统设置 → 隐私与安全性 → 辅助功能
- 勾选 Hammerspoon
4. 配置文件结构
~/.hammerspoon/
├── init.lua -- 主入口文件
└── modules/ -- 自定义模块目录
5. 示例配置
init.lua
内容如下:
-- 超实用快捷键:⌃⌥⌘ + D 显示桌面
hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'D', function()hs.osascript.applescript('tell application "Finder" to set desktop visible to true')
end)-- 窗口管理:⌃⌥ + 方向键快速调整窗口位置
hs.hotkey.bind({'ctrl', 'alt'}, 'Left', function()local win = hs.window.focusedWindow()win:moveToUnit({0, 0, 0.5, 1}) -- 左半屏
end)
3. 重载配置
点击菜单栏图标 → 「Reload Config」

二、实用代码片段
1. 重新加载配置
-- 示例快捷键:Ctrl+Alt+Cmd+R 重新加载配置
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "R", function()hs.reload()
end)
2. 快捷输入文字
-- 定义通用函数:发送文本并回车
-- 终极稳定版(带输入法状态检测)
function sendTextAndEnter(text)-- 检测是否中文输入法local isChinese = (hs.keycodes.currentLayout():find("Chinese") ~= nil)if isChinese then-- 中文模式:切换英文布局hs.keycodes.setLayout("U.S.")hs.timer.usleep(50000) -- 等待50msend-- 分段输入核心逻辑for i = 1, #text, 3 do -- 每3个字符一组hs.eventtap.keyStrokes(text:sub(i, i+2))hs.timer.usleep(50000) -- 组间延迟50msend-- 恢复中文布局(如果需要)hs.timer.delayed.new(0.2, function()if isChinese thenhs.keycodes.setLayout("Chinese")endhs.eventtap.keyStroke({}, "return")end):start()
endhs.hotkey.bind({"ctrl"}, "s", function() sendTextAndEnter("收到") end) -- Command+S → "收到"
hs.hotkey.bind({"ctrl"}, "h", function() sendTextAndEnter("好的") end) -- Command+H → "好的"
3. 打开网页
-- 定义通用函数:打开URL
function openURL(url)hs.execute('open "' .. url .. '"')
end
-- 绑定网页快捷键
hs.hotkey.bind({"cmd"}, "1", function() openURL("https://www.baidu.com") end) -- Command+1 → 百度
hs.hotkey.bind({"cmd"}, "2", function() openURL("https://blog.csdn.net/qq_33204709?type=blog") end) -- cmd+2 → 博客
hs.hotkey.bind({"cmd"}, "3", function() openURL("https://google.com/") end) -- Command+3 → 谷歌
hs.hotkey.bind({"cmd"}, "4", function() openURL("https://cn.bing.com/") end) -- Command+4 → 必应
5. 文字缩写填充
-- =============================================
-- 文本替换核心引擎
-- 特点:零误删、100%精确、支持所有输入法
-- =============================================-- 配置区(用户可修改)
local config = {triggers = {["sf"] = "SELECT * FROM ",["scf"] = "SELECT COUNT(*) FROM "},delay = 30000, -- 微秒级延迟 (30ms)maxBuffer = 10 -- 最大字符缓存
}-- 引擎核心(不要修改)
local buffer = ""
local isProcessing = falselocal function processBuffer()if isProcessing then return endisProcessing = truefor trigger, replacement in pairs(config.triggers) doif buffer:sub(-#trigger) == trigger then-- 精确删除(不使用循环避免误差)hs.eventtap.keyStrokes(string.rep("\b", #trigger))hs.timer.usleep(config.delay)-- 插入新内容hs.eventtap.keyStrokes(replacement)-- 重置状态buffer = ""isProcessing = falsereturn trueendendisProcessing = falsereturn false
end-- 事件监听(优化版)
local eventTap = hs.eventtap.new({hs.eventtap.event.types.keyDown}, function(e)-- 获取按键字符local char = hs.keycodes.map[e:getKeyCode()] or ""-- 过滤非字母键if not char:match("%a") thenbuffer = ""return falseend-- 更新缓冲区buffer = buffer .. charif #buffer > config.maxBuffer thenbuffer = buffer:sub(-config.maxBuffer)end-- 延迟处理确保稳定性hs.timer.doAfter(0.05, processBuffer) -- 50ms延迟return false
end)-- 安全启动
if hs.eventtap theneventTap:start()
elsehs.alert.show("初始化失败: 缺少eventtap模块", 2)
end
6. 窗口大小变化
-- 示例窗口管理:将当前窗口移到屏幕左侧
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Left", function()local win = hs.window.focusedWindow()local f = win:frame()local screen = win:screen()local max = screen:frame()f.x = max.xf.y = max.yf.w = max.w / 2f.h = max.hwin:setFrame(f)
end)-- 示例窗口管理:将当前窗口移到屏幕右侧
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Right", function()local win = hs.window.focusedWindow()local f = win:frame()local screen = win:screen()local max = screen:frame()f.x = max.x + (max.w / 2)f.y = max.yf.w = max.w / 2f.h = max.hwin:setFrame(f)
end)-- 按下ctrl+alt+x实现窗口最大化
hs.hotkey.bind({"ctrl", "cmd"}, "X", function()local win = hs.window.focusedWindow()if win then-- 最大化当前窗口win:maximize()elsehs.alert.show("没有聚焦的窗口")end
end)
7. 防沉迷提醒
-- 每30分钟提醒休息
workTimer = hs.timer.doEvery(30*60, function()hs.notify.new({title = "休息提醒",subTitle = "已经工作30分钟了",informativeText = "起来活动一下吧~"}):send()
end)
8. 剪切板历史
-- 需要安装clipboard插件
require("hs.clipboard")
hs.hotkey.bind({'cmd', 'shift'}, 'V', function()hs.clipboard.managerOnTop(true)
end)
9. 快速分屏(替代 Magnet)
-- 快速分屏
hs.hotkey.bind({'cmd', 'alt'}, 'Left', function()hs.window.focusedWindow():moveToUnit({0, 0, 0.5, 1})
end)hs.hotkey.bind({'cmd', 'alt'}, 'Right', function()hs.window.focusedWindow():moveToUnit({0.5, 0, 0.5, 1})
end)
10. 应用启动
-- ⌃⌥⌘ + V 打开 VSCode
hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'V', function()hs.application.launchOrFocus("Visual Studio Code")
end)
11. 电量监控
-- 显示电池状态
function showBattery()local battery = hs.battery.percentage()hs.alert.show("🔋 电量: " .. battery .. "%")
end
hs.hotkey.bind({'ctrl', 'cmd'}, 'B', showBattery)
12. 网络切换
-- 切换WiFi
function toggleWIFI()hs.execute("/usr/sbin/networksetup -setairportpower en0 toggle")
end
hs.hotkey.bind({'ctrl', 'cmd'}, 'W', toggleWIFI)
三、调试与维护
1. 控制台调试
# 打开调试控制台
hs.openConsole()
2. 常用命令
hs.reload() -- 重载配置
hs.alert("Hello!") -- 显示提示
hs.openAbout() -- 打开关于面板

3. 错误排查
- 查看控制台日志(菜单栏 → 「Console」)
- 检查权限设置
- 使用
hs.inspect(variable)
输出变量值
四、推荐插件及安装方式
1. 主流插件安装指南 🔌
所有插件需放置在 ~/.hammerspoon/Spoons/
目录下,然后在 init.lua
中加载:
-- 加载插件示例
hs.loadSpoon("PluginName")
第1步 SpoonInstall(插件管理器)
-- 安装步骤:
-- 1. 下载插件
hs.execute('curl -fsSL https://github.com/Hammerspoon/Spoons/raw/master/Spoons/SpoonInstall.spoon.zip -o /tmp/SpoonInstall.zip')
-- 2. 解压到插件目录
hs.execute('unzip -oq /tmp/SpoonInstall.zip -d ~/.hammerspoon/Spoons/')
-- 3. 在配置中启用
hs.loadSpoon("SpoonInstall")-- 使用示例:安装其他插件
spoon.SpoonInstall:andUse("ClipboardTool", {repo = "https://github.com/Hammerspoon/Spoons",hotkeys = true
})
第2步 WinWin(窗口管理增强)
# 手动安装
cd ~/.hammerspoon/Spoons/
git clone https://github.com/asmagill/hs._asm.undocumented.winwin.git WinWin.spoon
-- 配置示例
hs.loadSpoon("WinWin")
spoon.WinWin:bindHotkeys({left50 = {{"ctrl", "alt"}, "Left"},right50 = {{"ctrl", "alt"}, "Right"}
})
第3步 ClipboardTool(剪切板历史)
-- 通过 SpoonInstall 安装
spoon.SpoonInstall:andUse("ClipboardTool", {config = {max_items = 20,show_copied_alert = true},hotkeys = {toggle = {{"cmd", "shift"}, "V"}}
})
第4步 FadeLogo(状态栏动画)
# 手动安装
curl -fsSL https://github.com/Hammerspoon/Spoons/raw/master/Spoons/FadeLogo.spoon.zip -o /tmp/FadeLogo.zip
unzip /tmp/FadeLogo.zip -d ~/.hammerspoon/Spoons/
-- 使用示例
hs.loadSpoon("FadeLogo")
spoon.FadeLogo:start()
2. 插件管理技巧
自动更新插件
-- 添加到 init.lua
function updateSpoons()hs.execute("find ~/.hammerspoon/Spoons -maxdepth 1 -type d -exec git -C {} pull \\;")hs.reload()
end
hs.hotkey.bind({"cmd", "ctrl"}, "U", updateSpoons)
插件冲突排查
- 在控制台输入
hs.spoons.list()
查看已加载插件 - 使用
hs.spoons.use("PluginName", { lazy = true })
延迟加载 - 通过注释法隔离问题插件
💡 推荐组合:SpoonInstall + WinWin + ClipboardTool 满足90%日常需求
3. 社区资源
资源类型 | 链接 |
---|---|
官方插件库 | Hammerspoon/Spoons |
配置分享 | Awesome Hammerspoon |
插件开发指南 | Writing Spoons |
五、扩展资源
1. 官方文档
- Hammerspoon 官网
- API 文档
2. 精品配置
# 克隆流行配置方案
git clone https://github.com/ashfinal/awesome-hammerspoon ~/.hammerspoon
3. 小编配置
-- =============================================
-- 初始化 Hammerspoon 配置
-- =============================================
hs.loadSpoon("SpoonInstall")-- =============================================
-- 通用函数:发送文本并回车(终极稳定版)
-- 特点:带输入法状态检测,分段输入防止卡顿
-- =============================================
function sendTextAndEnter(text)-- 检测是否中文输入法local isChinese = (hs.keycodes.currentLayout():find("Chinese") ~= nil)if isChinese then-- 中文模式:切换英文布局hs.keycodes.setLayout("U.S.")hs.timer.usleep(50000) -- 等待50msend-- 分段输入核心逻辑for i = 1, #text, 3 do -- 每3个字符一组hs.eventtap.keyStrokes(text:sub(i, i+2))hs.timer.usleep(50000) -- 组间延迟50msend-- 恢复中文布局(如果需要)hs.timer.delayed.new(0.2, function()if isChinese thenhs.keycodes.setLayout("Chinese")endhs.eventtap.keyStroke({}, "return")end):start()
end-- =============================================
-- 通用函数:打开URL
-- =============================================
function openURL(url)hs.execute('open "' .. url .. '"')
end-- =============================================
-- 通用函数:启动应用程序
-- 注意:Windows路径需要转换为Mac路径
-- =============================================
function launchApp(appPath)local macPath = string.gsub(appPath, "D:\\Program Files", "/Applications")hs.execute('open "' .. macPath .. '"')
end-- =============================================
-- 快捷键绑定:常用短语
-- =============================================
hs.hotkey.bind({"ctrl"}, "s", function() sendTextAndEnter("收到") end) -- Command+S → "收到"
hs.hotkey.bind({"ctrl"}, "h", function() sendTextAndEnter("好的") end) -- Command+H → "好的"-- =============================================
-- 快捷键绑定:网页快速访问
-- =============================================
hs.hotkey.bind({"ctrl"}, "1", function() openURL("https://www.baidu.com") end) -- Command+1 → 百度
hs.hotkey.bind({"ctrl"}, "2", function() openURL("https://blog.csdn.net/qq_33204709?type=blog") end) -- cmd+2 → 博客
hs.hotkey.bind({"ctrl"}, "3", function() openURL("https://google.com/") end) -- Command+3 → 谷歌
hs.hotkey.bind({"ctrl"}, "4", function() openURL("https://cn.bing.com/") end) -- Command+4 → 必应-- =============================================
-- 快捷键绑定:媒体控制
-- =============================================
-- hs.hotkey.bind({"cmd"}, "6", function()
-- hs.execute('osascript -e "tell application \\"Music\\" to playpause"')
-- end) -- cmd+6 → 播放/暂停-- =============================================
-- 文本替换引擎
-- 特点:零误删、100%精确、支持所有输入法
-- =============================================
local config = {triggers = {["sf"] = "SELECT * FROM ",["scf"] = "SELECT COUNT(*) FROM "},delay = 30000, -- 微秒级延迟 (30ms)maxBuffer = 10 -- 最大字符缓存
}local buffer = ""
local isProcessing = falselocal function processBuffer()if isProcessing then return endisProcessing = truefor trigger, replacement in pairs(config.triggers) doif buffer:sub(-#trigger) == trigger then-- 精确删除(不使用循环避免误差)hs.eventtap.keyStrokes(string.rep("\b", #trigger))hs.timer.usleep(config.delay)-- 插入新内容hs.eventtap.keyStrokes(replacement)-- 重置状态buffer = ""isProcessing = falsereturn trueendendisProcessing = falsereturn false
endlocal eventTap = hs.eventtap.new({hs.eventtap.event.types.keyDown}, function(e)-- 获取按键字符local char = hs.keycodes.map[e:getKeyCode()] or ""-- 过滤非字母键if not char:match("%a") thenbuffer = ""return falseend-- 更新缓冲区buffer = buffer .. charif #buffer > config.maxBuffer thenbuffer = buffer:sub(-config.maxBuffer)end-- 延迟处理确保稳定性hs.timer.doAfter(0.05, processBuffer) -- 50ms延迟return false
end)-- 安全启动事件监听
if hs.eventtap theneventTap:start()
elsehs.alert.show("初始化失败: 缺少eventtap模块", 2)
end-- =============================================
-- 快捷键绑定:快速访问路径
-- =============================================
hs.hotkey.bind({"cmd"}, "e", function()local path = "/Users/acgkaka/my"local attr = hs.fs.attributes(path)local exists = attr ~= nilif exists thenhs.execute('open "' .. path .. '"')elsehs.alert.show("路径不存在: " .. path, 2)end
end)-- =============================================
-- 快捷键绑定:配置重载
-- =============================================
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "R", function()hs.reload()
end)-- =============================================
-- 窗口管理快捷键
-- =============================================-- 将当前窗口移到屏幕左侧
-- hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Left", function()
-- local win = hs.window.focusedWindow()
-- local f = win:frame()
-- local screen = win:screen()
-- local max = screen:frame()-- f.x = max.x
-- f.y = max.y
-- f.w = max.w / 2
-- f.h = max.h
-- win:setFrame(f)
-- end)-- -- 将当前窗口移到屏幕右侧
-- hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Right", function()
-- local win = hs.window.focusedWindow()
-- local f = win:frame()
-- local screen = win:screen()
-- local max = screen:frame()-- f.x = max.x + (max.w / 2)
-- f.y = max.y
-- f.w = max.w / 2
-- f.h = max.h
-- win:setFrame(f)
-- end)-- -- 窗口最大化
-- hs.hotkey.bind({"ctrl", "cmd"}, "X", function()
-- local win = hs.window.focusedWindow()
-- if win then
-- win:maximize()
-- else
-- hs.alert.show("没有聚焦的窗口")
-- end
-- end)-- =============================================
-- 通用应用程序启动函数
-- =============================================
function launchOrFocusApp(appPath, showNotification)-- 检查应用是否存在if not hs.fs.attributes(appPath) thenhs.notify.new({title="Hammerspoon 错误", informativeText="未找到应用程序: "..appPath}):send()return falseend-- 启动或聚焦应用程序hs.application.launchOrFocus(appPath)-- 显示通知(如果需要)if showNotification thenlocal appName = appPath:match("([^/]+)%.app$") or appPathhs.notify.new({title="Hammerspoon", informativeText="已启动 "..appName}):send()endreturn true
end-- =============================================
-- 应用程序快速启动快捷键
-- =============================================-- 启动All应用
hs.hotkey.bind({"ctrl", "cmd", "shift"}, "f", function()launchOrFocusApp("/Applications/All.app", true)
end)-- 启动终端
hs.hotkey.bind({"ctrl", "cmd", "shift"}, "c", function()launchOrFocusApp("/System/Applications/Utilities/Terminal.app", true)
end)-- =============================================
-- 初始化完成提示
-- =============================================
hs.alert.show("Hammerspoon 配置已加载")
💡 终极提示:Hammerspoon 的威力在于 Lua 脚本的无限可能,从简单的快捷键到复杂的自动化工作流,只有想不到,没有做不到!
整理完毕,完结撒花~🌻