目录
-
- [一、安装 Hammerspoon](#一、安装 Hammerspoon)
-
- [1. 官方安装(推荐)](#1. 官方安装(推荐))
- [2. 手动安装](#2. 手动安装)
- [3. 首次配置](#3. 首次配置)
- [4. 配置文件结构](#4. 配置文件结构)
- [5. 示例配置](#5. 示例配置)
- [3. 重载配置](#3. 重载配置)
- 二、实用代码片段
-
- [1. 重新加载配置](#1. 重新加载配置)
- [2. 快捷输入文字](#2. 快捷输入文字)
- [3. 打开网页](#3. 打开网页)
- [5. 文字缩写填充](#5. 文字缩写填充)
- [6. 窗口大小变化](#6. 窗口大小变化)
- [7. 防沉迷提醒](#7. 防沉迷提醒)
- [8. 剪切板历史](#8. 剪切板历史)
- [9. 快速分屏(替代 Magnet)](#9. 快速分屏(替代 Magnet))
- [10. 应用启动](#10. 应用启动)
- [11. 电量监控](#11. 电量监控)
- [12. 网络切换](#12. 网络切换)
- 三、调试与维护
-
- [1. 控制台调试](#1. 控制台调试)
- [2. 常用命令](#2. 常用命令)
- [3. 错误排查](#3. 错误排查)
- 四、推荐插件及安装方式
- 五、扩展资源
-
- [1. 官方文档](#1. 官方文档)
- [2. 精品配置](#2. 精品配置)
- [3. 小编配置](#3. 小编配置)

🚀 Hammerspoon 是 macOS 上最强大的自动化工具,通过 Lua 脚本实现:
- 全局快捷键自定义
- 窗口管理自动化
- 应用快速切换
- 系统状态监控
- 任意功能扩展
比 Karabiner 更轻量,比 BetterTouchTool 更自由!
一、安装 Hammerspoon
1. 官方安装(推荐)
bash
brew install --cask hammerspoon
2. 手动安装
在 官网下载 之后,双击打开 → 拖拽到 Applications
文件夹
3. 首次配置
- 打开应用后,菜单栏会出现「🔨」图标
- 点击图标 → 「Open Config」打开配置文件目录
- 系统会要求授予「辅助功能」权限:
系统设置 → 隐私与安全性 → 辅助功能
- 勾选 Hammerspoon
4. 配置文件结构
sql
~/.hammerspoon/
├── init.lua -- 主入口文件
└── modules/ -- 自定义模块目录
5. 示例配置
init.lua
内容如下:
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. 重新加载配置
lua
-- 示例快捷键:Ctrl+Alt+Cmd+R 重新加载配置
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "R", function()
hs.reload()
end)
2. 快捷输入文字
lua
-- 定义通用函数:发送文本并回车
-- 终极稳定版(带输入法状态检测)
function sendTextAndEnter(text)
-- 检测是否中文输入法
local isChinese = (hs.keycodes.currentLayout():find("Chinese") ~= nil)
if isChinese then
-- 中文模式:切换英文布局
hs.keycodes.setLayout("U.S.")
hs.timer.usleep(50000) -- 等待50ms
end
-- 分段输入核心逻辑
for i = 1, #text, 3 do -- 每3个字符一组
hs.eventtap.keyStrokes(text:sub(i, i+2))
hs.timer.usleep(50000) -- 组间延迟50ms
end
-- 恢复中文布局(如果需要)
hs.timer.delayed.new(0.2, function()
if isChinese then
hs.keycodes.setLayout("Chinese")
end
hs.eventtap.keyStroke({}, "return")
end):start()
end
hs.hotkey.bind({"ctrl"}, "s", function() sendTextAndEnter("收到") end) -- Command+S → "收到"
hs.hotkey.bind({"ctrl"}, "h", function() sendTextAndEnter("好的") end) -- Command+H → "好的"
3. 打开网页
lua
-- 定义通用函数:打开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. 文字缩写填充
lua
-- =============================================
-- 文本替换核心引擎
-- 特点:零误删、100%精确、支持所有输入法
-- =============================================
-- 配置区(用户可修改)
local config = {
triggers = {
["sf"] = "SELECT * FROM ",
["scf"] = "SELECT COUNT(*) FROM "
},
delay = 30000, -- 微秒级延迟 (30ms)
maxBuffer = 10 -- 最大字符缓存
}
-- 引擎核心(不要修改)
local buffer = ""
local isProcessing = false
local function processBuffer()
if isProcessing then return end
isProcessing = true
for trigger, replacement in pairs(config.triggers) do
if buffer:sub(-#trigger) == trigger then
-- 精确删除(不使用循环避免误差)
hs.eventtap.keyStrokes(string.rep("\b", #trigger))
hs.timer.usleep(config.delay)
-- 插入新内容
hs.eventtap.keyStrokes(replacement)
-- 重置状态
buffer = ""
isProcessing = false
return true
end
end
isProcessing = false
return 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") then
buffer = ""
return false
end
-- 更新缓冲区
buffer = buffer .. char
if #buffer > config.maxBuffer then
buffer = buffer:sub(-config.maxBuffer)
end
-- 延迟处理确保稳定性
hs.timer.doAfter(0.05, processBuffer) -- 50ms延迟
return false
end)
-- 安全启动
if hs.eventtap then
eventTap:start()
else
hs.alert.show("初始化失败: 缺少eventtap模块", 2)
end
6. 窗口大小变化
lua
-- 示例窗口管理:将当前窗口移到屏幕左侧
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)
-- 按下ctrl+alt+x实现窗口最大化
hs.hotkey.bind({"ctrl", "cmd"}, "X", function()
local win = hs.window.focusedWindow()
if win then
-- 最大化当前窗口
win:maximize()
else
hs.alert.show("没有聚焦的窗口")
end
end)
7. 防沉迷提醒
lua
-- 每30分钟提醒休息
workTimer = hs.timer.doEvery(30*60, function()
hs.notify.new({
title = "休息提醒",
subTitle = "已经工作30分钟了",
informativeText = "起来活动一下吧~"
}):send()
end)
8. 剪切板历史
lua
-- 需要安装clipboard插件
require("hs.clipboard")
hs.hotkey.bind({'cmd', 'shift'}, 'V', function()
hs.clipboard.managerOnTop(true)
end)
9. 快速分屏(替代 Magnet)
lua
-- 快速分屏
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. 应用启动
lua
-- ⌃⌥⌘ + V 打开 VSCode
hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'V', function()
hs.application.launchOrFocus("Visual Studio Code")
end)
11. 电量监控
lua
-- 显示电池状态
function showBattery()
local battery = hs.battery.percentage()
hs.alert.show("🔋 电量: " .. battery .. "%")
end
hs.hotkey.bind({'ctrl', 'cmd'}, 'B', showBattery)
12. 网络切换
lua
-- 切换WiFi
function toggleWIFI()
hs.execute("/usr/sbin/networksetup -setairportpower en0 toggle")
end
hs.hotkey.bind({'ctrl', 'cmd'}, 'W', toggleWIFI)
三、调试与维护
1. 控制台调试
bash
# 打开调试控制台
hs.openConsole()
2. 常用命令
lua
hs.reload() -- 重载配置
hs.alert("Hello!") -- 显示提示
hs.openAbout() -- 打开关于面板

3. 错误排查
- 查看控制台日志(菜单栏 → 「Console」)
- 检查权限设置
- 使用
hs.inspect(variable)
输出变量值
四、推荐插件及安装方式
1. 主流插件安装指南 🔌
所有插件需放置在 ~/.hammerspoon/Spoons/
目录下,然后在 init.lua
中加载:
lua
-- 加载插件示例
hs.loadSpoon("PluginName")
第1步 SpoonInstall(插件管理器)
lua
-- 安装步骤:
-- 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(窗口管理增强)
bash
# 手动安装
cd ~/.hammerspoon/Spoons/
git clone https://github.com/asmagill/hs._asm.undocumented.winwin.git WinWin.spoon
lua
-- 配置示例
hs.loadSpoon("WinWin")
spoon.WinWin:bindHotkeys({
left50 = {{"ctrl", "alt"}, "Left"},
right50 = {{"ctrl", "alt"}, "Right"}
})
第3步 ClipboardTool(剪切板历史)
lua
-- 通过 SpoonInstall 安装
spoon.SpoonInstall:andUse("ClipboardTool", {
config = {
max_items = 20,
show_copied_alert = true
},
hotkeys = {
toggle = {{"cmd", "shift"}, "V"}
}
})
第4步 FadeLogo(状态栏动画)
bash
# 手动安装
curl -fsSL https://github.com/Hammerspoon/Spoons/raw/master/Spoons/FadeLogo.spoon.zip -o /tmp/FadeLogo.zip
unzip /tmp/FadeLogo.zip -d ~/.hammerspoon/Spoons/
lua
-- 使用示例
hs.loadSpoon("FadeLogo")
spoon.FadeLogo:start()
2. 插件管理技巧
自动更新插件
lua
-- 添加到 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. 官方文档
2. 精品配置
bash
# 克隆流行配置方案
git clone https://github.com/ashfinal/awesome-hammerspoon ~/.hammerspoon
3. 小编配置
lua
-- =============================================
-- 初始化 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) -- 等待50ms
end
-- 分段输入核心逻辑
for i = 1, #text, 3 do -- 每3个字符一组
hs.eventtap.keyStrokes(text:sub(i, i+2))
hs.timer.usleep(50000) -- 组间延迟50ms
end
-- 恢复中文布局(如果需要)
hs.timer.delayed.new(0.2, function()
if isChinese then
hs.keycodes.setLayout("Chinese")
end
hs.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 = false
local function processBuffer()
if isProcessing then return end
isProcessing = true
for trigger, replacement in pairs(config.triggers) do
if buffer:sub(-#trigger) == trigger then
-- 精确删除(不使用循环避免误差)
hs.eventtap.keyStrokes(string.rep("\b", #trigger))
hs.timer.usleep(config.delay)
-- 插入新内容
hs.eventtap.keyStrokes(replacement)
-- 重置状态
buffer = ""
isProcessing = false
return true
end
end
isProcessing = false
return 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") then
buffer = ""
return false
end
-- 更新缓冲区
buffer = buffer .. char
if #buffer > config.maxBuffer then
buffer = buffer:sub(-config.maxBuffer)
end
-- 延迟处理确保稳定性
hs.timer.doAfter(0.05, processBuffer) -- 50ms延迟
return false
end)
-- 安全启动事件监听
if hs.eventtap then
eventTap:start()
else
hs.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 ~= nil
if exists then
hs.execute('open "' .. path .. '"')
else
hs.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) then
hs.notify.new({title="Hammerspoon 错误", informativeText="未找到应用程序: "..appPath}):send()
return false
end
-- 启动或聚焦应用程序
hs.application.launchOrFocus(appPath)
-- 显示通知(如果需要)
if showNotification then
local appName = appPath:match("([^/]+)%.app$") or appPath
hs.notify.new({title="Hammerspoon", informativeText="已启动 "..appName}):send()
end
return 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 脚本的无限可能,从简单的快捷键到复杂的自动化工作流,只有想不到,没有做不到!
整理完毕,完结撒花~🌻