你坐下来研究一个功能,然后唤醒你的Mac。哦,嘿,Slack打开了。你决定快速检查一下。30分钟后,你想起了你想做的事情,于是关闭了Slack。
然后你切换到终端,cd 到项目目录,在你最喜欢的文本编辑器中打开项目,运行服务器,在浏览器中打开应用程序,并按你喜欢的方式安排屏幕上的所有窗口。然后你收到一封邮件通知,于是你退出所有的通信应用程序,并打开勿扰。
或者你管理多个项目,如博客、产品、播客和现场直播。每个项目都有自己的背景,包括应用布局、开发和macOS设置。而你发现自己每次都在手动设置一切。
如果这听起来很熟悉,你会想把Bunch应用加入你的自动化工具箱。
不像其他基于GUI的自动化应用,如Alfred、Keyboard Maestro和Shortcuts,Bunch只是一个文本文件。Bunch的文档非常好,包括了一些样本的Bunch,让你开始使用。
我为自己创建了一个叫Coding.bunch ,展示了一些很酷的功能和语法。下面是完整的文件,我将一步一步地介绍。这里有一段视频,展示了它的运行情况:
// Hide all visible apps
@@
<quit_comms.snippet
project = ?[
Blog
Ruby on Mac Site
] "Whatcha workin' on?"
<<#${project}
soundcloud = $ osascript ~/Documents/Bunches/search_open_tabs.applescript "soundcloud"
___
#[Blog]
Tower
- ~/projects/monfresh/blog
iTerm
- [blog\\n]
- {shift-command-d}
port = 4567
path = archives
<<#Arrange Safari and Sublime
#[Ruby on Mac Site]
iTerm
- {"cd ~/projects/rubyonmac/rubyonmac-site" return "git pull && bundle && bin/bridgetown start" return }
port = 4000
<<#Arrange Safari and Sublime
#[Arrange Safari and Sublime]
if ${soundcloud}
(log soundcloud is already open)
else
%Safari
- https://soundcloud.com/discover/sets/new-for-you::monfresh
end
%Safari
- (pause 2)
- http://localhost:${port}/${path}
- {command-r}
@Sublime Text
* tell application "Moom" to arrange windows according to snapshot "Safari and Sublime Side by Side"
Brett Terpstra(Bunch的作者)还为各种IDE创建了包括高亮和补全的软件包,在Sublime Text中使用Bunch时,这很好。他还慷慨地分享了他为Bunch设计的Rouge词典,这样我就可以在这个页面上进行语法高亮。
这个Bunch做的第一件事是用@@ 命令隐藏所有可见的应用程序。或者,为了从一个干净的地方开始,你可以退出一切:
(quit everything)
你也可以排除某些应用程序的退出:
(quit everything except Safari)
布雷特还支持一些有趣的退出一切命令的变化。
看看你可以运行的所有命令。
接下来,我想尝试一下一次性退出所有分心的东西,对我来说,这些都是通讯类的应用程序。考虑到其他组的可重用性,我创建了一个退出所有这些应用程序的片段,并将其命名为quit_comms.snippet:
!Discord
!Mail
!Messages
!Slack
!Stellar
!Telegram
!Twitter
!WhatsApp
然后我在我的Coding Bunch中这样调用它:
<quit_comms.snippet
一个片段文件可以用除.bunch 以外的任何扩展名命名,并且应该存储在你的Bunches文件夹中(我想默认是~/Documents/Bunches 。)
起初,我以为我可以创建一个Comms.bunch ,启动所有这些应用程序,并且我可以告诉它在我的Coding.bunch 中关闭所有这些应用程序,就像Bunch文档中的Podcasting样本Bunch一样:
!Comms.bunch
然而,这不是Bunch的工作方式。如果这些通信应用程序之一已经在运行,启动Coding bunch不会退出该应用程序。而当Coding 束被停止时,Comms 束会自动启动并打开所有的通信应用!这绝对不是我想要的。这绝对不是我想要的。
Coding 束的下一部分显示了一个提示,问我在哪个项目上工作:
project = ?[
Blog
Ruby on Mac Site
] "Whatcha workin' on?"
<<#${project}
在这里,我们要把项目的名称分配给一个project 变量,然后告诉Bunch运行该项目特有的动作。每个项目的动作都被定义在一个 "片段 "中。片段头可以以#,-,=, 或> 开始,后面是用方括号括起来的片段名称。在这个例子中,有两个片段:#[Blog] 和#[Ruby on Mac Site] 。
你可以用<<# 跟随片段名称来调用 Bunch 内的片段,你甚至可以用~ 跟随秒数来给该片段添加延迟。比如说:
<<#Blog ~2
让我们看一下Blog这个片段:
#[Blog]
iTerm
- [blog\\n]
- {shift-command-d}
port = 4567
path = archives
<<#Arrange Safari and Sublime
首先,它启动了iTerm,然后如果你想在一个应用程序中执行某些动作,你就用- ,后面跟着一个动作,开始一个新的行。要输入一个字符串,你就把它放在方括号里。在这个例子中,我想让Bunch输入blog ,然后按回车键:
- [blog\\n]
blog 是我的Fish shell中的一个别名,用于运行这些命令:
cd ~/projects/monfresh/blog && git pull && chruby (cat .ruby-version) \
&& bundle && subl . && bundle exec middleman serve
接下来,我想在iTerm中按shift-⌘-D ,在当前窗格下打开一个新窗格。这样,我就可以在上面的窗格中运行服务器,而在下面的窗格中,我可以在需要的时候运行命令。要告诉Bunch发送按键,你可以用大括号把它们括起来:
- {shift-command-d}
你也可以把打字和击键结合起来,如Ruby on Mac Site 片段所示:
- {"cd ~/projects/rubyonmac/rubyonmac-site" return "git pull && bundle && bin/bridgetown start" return }
最后,我调用另一个叫Arrange Safari and Sublime 的片段,并传入port 的值4000和path 的值 "archives":
port = 4000
path = archives
<<#Arrange Safari and Sublime
下面是这个片段的样子:
#[Arrange Safari and Sublime]
if ${soundcloud}
(log soundcloud is already open)
else
%Safari
- https://soundcloud.com/discover/sets/new-for-you::monfresh
end
%Safari
- (pause 2)
- http://localhost:${port}/${path}
- {command-r}
@Sublime Text
* tell application "Moom" to arrange windows according to snapshot "Safari and Sublime Side by Side"
由于条件式和变量的工作方式,这个片段最后很难搞清楚,所以要注意。
我喜欢听SoundCloud为我策划的 "The Upload "播放列表,当我测试打开SoundCloud URL时,它已经在播放,但当我运行Bunch时,它停止了播放,因为它刷新了页面。😢
这时我研究了Bunch的条件逻辑和变量。我没有找到一个内置的方法来检查一个浏览器标签是否已经打开,所以我问DuckDuckGo如何用AppleScript做到这一点。我发现的例子基本相同,但它们做的事比我需要的多,所以我把它修剪下来,并保存在我的Bunches文件夹中一个名为search_open_tabs.applescript 的文件中:
on run argv
set searchText to (item 1 of argv)
tell application "Safari"
set winlist to every window
set tabmatchlist to {}
repeat with win in winlist
if (count of tabs of win) is not equal to 0 then
set tablist to every tab of win
repeat with t in tablist
if (searchText is in (name of t as string)) or (searchText is in (URL of t as string)) then
set end of tabmatchlist to t
end if
end repeat
end if
end repeat
if (count of tabmatchlist) = 1 then
return true
else
return false
end if
end tell
end run
起初,我尝试在#[Arrange Safari and Sublime] 片段中定义变量:
#[Arrange Safari and Sublime]
soundcloud = $ osascript ~/Documents/Bunches/search_open_tabs.applescript "soundcloud"
然后像这样使用它:
if soundcloud
(log soundcloud is already open)
else
%Safari
- https://soundcloud.com/discover/sets/new-for-you::monfresh
end
但由于某些原因,Bunch不喜欢这样。奇怪的是,Bunch可以看到这个值是true ,但它仍然会执行else 语句。下面是我如何在调试中添加日志的:
(log soundcloud is ${soundcloud})
if soundcloud
(log soundcloud is ${soundcloud})
(log soundcloud is already open)
else
(log soundcloud is ${soundcloud})
%Safari
- https://soundcloud.com/discover/sets/new-for-you::monfresh
end
于是我又尝试了变量名称的变化和比较:
if $soundcloudif ${soundcloud}if soundcloud is trueif soundcloud is "true"else if soundcloud is false
等等,但都没有成功。这时我意识到,也许这个变量需要在片段之外定义,这样就成功了!这就是我的想法。
如果你滚动到完整的文件,你会看到soundcloud 变量被定义在任何片段之前:
<<#${project}
soundcloud = $ osascript ~/Documents/Bunches/search_open_tabs.applescript "soundcloud"
___
#[Blog]
...
使用条件语句需要注意的另一件事是,缩进需要4个空格
接下来,我告诉Safari在用先前调用片段时传入的端口号和路径打开localhost之前暂停2秒。暂停的原因是我想让localhost成为最后打开的标签,但是Bunch一直先打开它,尽管它在Bunch文件中排在SoundCloud之后。
然后我发送⌘-R 键入来刷新页面,因为有时它在服务器完成加载之前就访问了localhost。在Safari之前的% 告诉Bunch在关闭Bunch后保持Safari打开。否则,任何由Bunch打开的应用程序都会在Bunch关闭时被退出。
这里又是Safari的部分:
%Safari
- (pause 2)
- http://localhost:${port}/${path}
- {command-r}
然后我用@ 前缀聚焦Sublime Text,最后,我运行一个AppleScript内联,告诉Moom将Safari安排在屏幕的右半部分,而Sublime Text安排在左半部分:
@Sublime Text
* tell application "Moom" to arrange windows according to snapshot "Safari and Sublime Side by Side"
我希望能够发送Moom的键盘快捷键,我有向左或向右发送窗口的功能,但看起来Bunch并不支持。我认为按键必须在应用程序的上下文中发送,而且似乎按键必须被应用程序识别,由于⌃-⌥-⌘-← ,不存在Safari或Sublime的键盘快捷键,我在Bunch的日志中得到一个错误,说是这样:
Could not convert control-option-command-left into key codes
因此,对于窗口管理,我不得不按照我想要的方式将Safari和Sublime并排设置,然后在Moom中保存一个快照。
就这样了!我现在经常使用这个邦奇,它的效果很好。我需要做的一个改进是,在运行git pull 之前,先检查Git是否干净,因为如果有未缓存的修改,git pull 会失败,服务器也不会启动。
默认情况下,要启动和停止Bunches,你必须通过菜单栏的图标来进行。为了加快速度,Bunch通过提供它自己的x-bunch: URL方案,使它很容易与其他自动化工具整合。
我选择在Keyboard Maestro中设置一个热键,当我按下shift-control-option-command-C ,就会打开x-bunch://open?bunch=Coding 。下面是这个宏的模样:

为了使这一做法更上一层楼,我使用Karabiner-Elements将大写锁定键变成一个超级键,将shift-control-option-command 。这样一来,我只需按下caps lock 和C ,就可以打开Coding Bunch。
使用 Bunch 建立一个直播流
我在我创建的另一个Bunch中遇到了一些问题,希望你能注意到这些问题。没有人在问题追踪或讨论中报告这些问题,所以我不确定它们是错误还是我的问题。
我最近开始在我的YouTube频道上进行现场编码,以体现公共建设的精神。到目前为止,我一直在研究Ruby on Mac,并使我的日常和业务工作流程自动化。我还计划分享我如何维护Rails应用程序,并录制我的自动化教程的视频版本。
为了减少在直播过程中不小心在屏幕上显示敏感或个人的东西的机会,我喜欢退出所有的应用程序,除了我将要使用的那些,通常是Sublime Text、iTerm和Safari。
我还喜欢隐藏所有的桌面图标,Ecamm Live,我用来流媒体的应用程序,有一个偏好,在我分享我的屏幕时自动隐藏桌面图标。我还注意到Bunch有一个做同样事情的命令,并想试试它,尽管我在使用Ecamm Live时不需要它:
(hide desktop)
然而,它对我来说并不奏效。我在我的M1 Macbook Air和英特尔iMac上试过,都是运行macOS 12.5(蒙特利)。我甚至用Bunch试过,它的唯一动作是隐藏桌面。两台Mac都在运行最新的Bunch版本:1.4.9(148)。
如果我告诉Bunch直接运行隐藏桌面图标的命令,然后在Bunch关闭时恢复这些图标,就能成功:
$ defaults write com.apple.finder CreateDesktop -bool false && killall Finder
!$ defaults write com.apple.finder CreateDesktop -bool true && killall Finder
正如你从上面的例子中猜到的那样,要在Bunch关闭时执行一个动作,你要在它前面加上一个感叹号。文档中有更多关于关闭时运行方式的例子。
下面是我创建的完整的直播Bunch,然后我将对它进行复习,以及一些让我绊倒的事情:
---
title: 🎥Livestream
open on: Mon 12:30pm, Wed 12:30pm
---
# Close all Finder windows
Finder
- XX
(quit everything except Safari, Moom, Keyboard Maestro, iTerm)
(hide desktop)
# Turn on Do Not Disturb
(dnd on)
Ecamm Live
iTerm
# Ask about specific project, handle setup
?{
Ruby on Mac => <<#Ruby on Mac
Rails app maintenance => <<#Ohana
} "Whatcha workin' on?"
# Quit the bandwidth heavy apps
$ killall Dropbox
$ /Library/Backblaze.bzpkg/bztransmit -pausebackup
# Focus ECamm Live, hiding other apps
@ECamm Live
# Turn Backblaze and Dropbox back on when this Bunch closes
!$ /Library/Backblaze.bzpkg/bztransmit -completesync
## Double negatives (!!) to launch apps and other Bunches when closing
!!Dropbox
___
#[Ruby on Mac]
Tower
- ~/projects/rubyonmac/rom-ultimate
$ subl ~/projects/rubyonmac/rom-ultimate
#[Ohana]
Tower
- ~/projects/codeforamerica/ohana-api
iTerm
- {command-t}
- [cfaoa\\n]
类似于Coding.bunch ,这个提示我将在现场编码的项目:
?{
Ruby on Mac => <<#Ruby on Mac
Rails app maintenance => <<#Ohana
} "Whatcha workin' on?"
不同的是,这使用的是Hash而不是Array,这允许下拉标签有一个与Bunch中的片段不同的名字:
这只是为了向你展示设置这些提示的不同方法。在现实中,我对标签和片段使用相同的名字。
当我第一次测试这一点时,它并不工作。在日志中,它一直说它找不到一个匹配的片段。我花了一些时间才意识到我做错了什么,那就是在片段上方使用3个破折号(--- ),而不是3个下划线(___ )。由于Sublime Text中的语法高亮,我知道出了问题,它关闭了片段中的所有高亮,但我无法找出原因
文档中提到片段分隔符应该是3个下划线,但我错过了,而且为了创建Coding.bunch ,我从文档中的样本Bunches中复制和粘贴,所以我从来没有打过3个下划线的经验。这提醒了我在跟随教程时直接自己输入代码而不是复制和粘贴的重要性。
你可以做的另一件很酷的事情是添加前言,比如自定义Bunch菜单栏的标签,以及告诉Bunch在某些日子和时间打开(和/或关闭)。当我流媒体时,通常是在下午1点到2点半之间,所以我把这个Bunch设置为提前30分钟打开,这样我就可以确保一切都在工作,并准备好了。
---
title: 🎥Livestream
open on: Mon 12:30pm, Wed 12:30pm
---
Bunch的其余部分应该是简单明了的,但我想指出最后一个可能的错误。Bunch 1.4.9版的发行说明说,"/opt/homebrew/bin现在包括在M1用户使用shell脚本的默认路径中。" 然而,下面的subl 命令在我的M1 Mac上运行livestream Bunch时失败了。它在我的英特尔iMac上工作正常:
$ subl ~/projects/rubyonmac/rom-ultimate
当我运行which subl ,我得到/opt/homebrew/bin/subl ,所以我不确定发生了什么。我将在Bunch的GitHub repo中打开一个bug,并更新这个帖子。这没什么大不了的,因为我可以告诉Sublime Text来打开它,或者通过iTerm来做:
Sublime Text
- ~/projects/rubyonmac/rom-ultimate
或
iTerm
- {command-t}
- {"subl ~/projects/rubyonmac/rom-ultimate" return}
这就结束了!我希望我给了你灵感,让你尝试一下Bunch。如果你这样做,请告诉我。
最后,我想说的是,如果你很快就要建立一个新的Mac,你可能对Ruby on Mac的 "终极 "版本感兴趣。只需一个命令,它就能按你喜欢的方式设置你的Mac,包括所有的开发工具、Mac应用程序、macOS偏好和GitHub仓库。它附带了一个Brewfile ,其中包含数百个开发工具和Mac应用程序,包括本篇文章中提到的所有应用程序。Backblaze, Bunch, Ecamm Live, iTerm2, Karabiner-Elements, Sublime Text, andTower.