Mac 终端神器 iTerm2 新增 Python API

iTerm2
的文档页面显示,其最新测试版增加了
Python API。具体来说就是,iTerm2 提供了一个 Python
包,通过它我们可以轻松编写控制 iTerm2 并扩展其行为的 Python 脚本。

摆脱瞎忙人生,从自动化做起。作为一名职场人,有没有为忘发汇报邮件而烦恼过,有没有因繁忙的工作而丢三落四过;作为一名程序员,有没为每天早上
pull
代码后漫长的编译时间而烦躁过,有没有为各种无脑的系统配置而无聊过。每天我们都在这些重复枯燥的事情中,浪费着我们宝贵的生命。如何提高工作效率成为我们至关重要的事情
,这篇文件将会带领我们通过自动化的方式,来有效地提高我们的工作效率。

澳门新萄京官方网站 1

本文大部内容是基于 MacOS 环境的,非 Mac 党也可以借鉴其中的一些思想。

当然,该功能目前尚处于 Beta 阶段,API 可能偶尔会发生变化。

Shell 与 Shell Script

在谈自动化之前,我们先来了解下 Shell Script
这门有点古老而又非常强大的脚本语言,首先我们要分清一个概念性的问题,Shell
和 Shell Script 之间的区别:

Shell
是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

Shell 脚本(Shell Script),是一种为 Shell 编写的脚本程序。

这里先给大家科普下概念,但大多的时候,我们都统称 Shell Script 为
shell,文中的其它地方的 “shell” 指的同样是 Shell Script。关于 shell
的基础语法,并不在本文的讨论范围内,但在开始实践如何通过 shell
来提高我们工作效率之前,我来我简单介绍 shell 几个常用而又强大的功能。

示例代码

常用串联命令

管道是 shell
中非常常用的功能之一,它允许不同脚本、命令之间互相传递数据,例如:

ls | grep 'pars'

该命令意思是将 ls 输出的内容传递到 grep ‘pars’ 命令,grep 会把包含
‘pars’ 的内容过虑出来。我们再举个栗子,通过 shell 获取 git
仓库里中的当前分支名:

currentBranch=`git branch | grep "*"`currentBranch=${currentBranch/* /}

大多数 UNIX
系统命令从终端接受输入并将所产生的输出发送回终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下是终端。同样,一个命令通常将其输出写入到标准输出,默认情况下也是终端。如果你需要修改输入或输出,就需要使用到重定向功能。

命令 说明
command > file 将输出重定向到 file。
command < file 将输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。

输出重定向,指将一条命令的输入位置重新定义,举个例子:

ls > ls.txt

ls 输出结果应显示在终端,而上面命令将 ls 的输出结果写到 ls.txt
这个文件中。需要注意,用 > 重定向到 ls.txt
文件默认是覆盖的,如果需要用追加的方式写入文件,则需要使用 >>:

pwd >> ls.txt

输入重定向:

pbcopy < ls.txt

上面的命令的意思是,将 ls.txt 的内容复制到粘贴板。

之所以简单介绍这几个常用功能,是因为如果不懂这些功能,会影响理解下面的内容。如果你对
shell 脚本不熟悉,建议你抽空学习一下,日常工作中,用到 shell
的频率还是非常高的。

Function Key
Tabs(通过功能键切换选项卡)

实践是检验学习的唯一标准

简单聊完 shell 这几个常用而又强大的功能后,我们开始实践如何通过 shell
来提高工作效率。

因我日常工作的需要,电脑需要配置双网卡,这里以配置电脑的双网卡为例,先附上部分配置脚本:

inside_ssid="WIFINAME";en0_ssid="$(networksetup -getairportnetwork en0 | sed 's/.*[:] //')"if [ "$en0_ssid" != "$inside_ssid" ]; then echo -n "请先将内网卡设置到$inside_ssid"else inside_adaptor_index="$(netstat -rn|grep default |grep en0 -n | cut -d: -f1)" if [ "$inside_adaptor_index" = 1 ]; then echo -n "请开启外网卡,且服务顺序在内网卡前" else inside_gateway="$(netstat -rn|grep default |grep en0|awk '{print $2}')" sudo echo "刷新配置成功"; sudo route -n delete -net 10.*.*.128; sudo route -n delete -net 10; sudo route -n delete -net 30; sudo route -n add -net 10.*.*.128 $inside_gateway; sudo route -n add -net 10 $inside_gateway; sudo route -n add -net 30 $inside_gateway fifi

这里不解析这些脚本的作用,我们的重点在于如何进一步提高效率上。由于每次重启电脑,都需重新配置双网卡,才能正常同时使用内外网,也就是说,每次重启电脑需要重新执行一次这个操作。懒是人类的天性,如果我们连执行一下脚本也不想干的话,那么我们需要怎么做呢?

我们可以在在电脑开机时设置自启动任务。首先我需要创建一个 plist 文件:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>LaunchOnlyOnce</key> <true/> <key>Label</key> <string>com.dengyonghao.xxxx</string> <key>ProgramArguments</key> <array> <string>/Users/xxxxx/work/Tools/xxxx.sh</string> </array></dict></plist>

简单解析下各个 key 的作用,LaunchOnlyOnce
指定开机后只执行一次,不指定则每隔一段时间就会执行一次。Label 指定该
plist 的唯一标识。ProgramArguments
则是设置执行脚本的路径,指向你想要启动时自动执行的脚本路径。

完成上面操作后,把创建的 plist 文件拷贝到
~/Library/LaunchAgents目录下,表示仅对当前用户生效,然后执行:

sudo launchctl load ~/Library/LaunchAgents/xxxxxx.plist

将这个文件注册到系统中,然后电脑启动时就会自动执行,完成双网上的配置。这里有一个问题,如果启动时自动执行上面的双网卡配置脚本,真的能正常运行吗?

事实上是不行的,因为 sudo
命令需要你输入管理员密码,但你并没有地方输入,所以是没有权限进行这些操作的。故我们需要用到
Apple Script,从而引出下一节的内容,这里先附上 Apple Script 脚本:

do shell script "inside_ssid="WIFINAME";en0_ssid="$(networksetup -getairportnetwork en0 | sed 's/.*[:] //')"if [ "$en0_ssid" != "$inside_ssid" ]; then echo -n "请先将内网卡设置到$inside_ssid"else inside_adaptor_index="$(netstat -rn|grep default |grep en0 -n | cut -d: -f1)" if [ "$inside_adaptor_index" = 1 ]; then echo -n "请开启外网卡,且服务顺序在内网卡前" else inside_gateway="$(netstat -rn|grep default |grep en0|awk '{print $2}')" sudo echo "刷新配置成功"; sudo route -n delete -net 10.*.*.128; sudo route -n delete -net 10; sudo route -n delete -net 30; sudo route -n add -net 10.*.*.128 $inside_gateway; sudo route -n add -net 10 $inside_gateway; sudo route -n add -net 30 $inside_gateway fifi" with administrator privileges

这里只是用 Apple Script
来提供一个图形界面来要求用户先输入管理员密码,才允许他继续执行脚本。脚本使用了
Apple Script,故开机启动项的 plist 的文件也要做相应的变化:

<key>ProgramArguments</key><array> <string>osascript</string> <string>/Users/xxxx/work/Tools/xxxx.scpt</string></array>

关于 Apple Script
的内容将在下一节详细介绍,这里我们先思考一个问题:设置开机执行后,就能解决上面说的痛点了吗?

事实上并不能解决所有问题,比如说,开机的时候外置网卡没有插上,那么自动执行的脚本就会配置失败,这时就又回到最初的场景,需要我们去找到这个脚本文件,然后通过终端执行。那么我们可以怎么去优化这个流程呢?我们带着这个问题,继续后面的内容,然后来再回来解决它。

#!/usr/bin/env python3

import asyncio
import iterm2

async def main(connection):
    app = await iterm2.async_get_app(connection)
    keycodes = [ iterm2.Keycode.F1,
                 iterm2.Keycode.F2,
                 iterm2.Keycode.F3,
                 iterm2.Keycode.F4,
                 iterm2.Keycode.F5,
                 iterm2.Keycode.F6,
                 iterm2.Keycode.F7,
                 iterm2.Keycode.F8,
                 iterm2.Keycode.F9,
                 iterm2.Keycode.F10,
                 iterm2.Keycode.F11,
                 iterm2.Keycode.F12 ]
    async def keystroke_handler(connection, keystroke):
        if keystroke.modifiers == [ iterm2.Modifier.FUNCTION ]:
            try:
              fkey = keycodes.index(keystroke.keycode)
              if fkey >= 0 and fkey < len(app.current_terminal_window.tabs):
                  await app.current_terminal_window.tabs[fkey].async_select()
            except:
              pass


    pattern = iterm2.KeystrokePattern()
    pattern.forbidden_modifiers.extend([iterm2.Modifier.CONTROL,
                                        iterm2.Modifier.OPTION,
                                        iterm2.Modifier.COMMAND,
                                        iterm2.Modifier.SHIFT,
                                        iterm2.Modifier.NUMPAD])
    pattern.required_modifiers.extend([iterm2.Modifier.FUNCTION])
    pattern.keycodes.extend(keycodes)

    async def monitor():
        async with iterm2.KeystrokeMonitor(connection) as mon:
            while True:
                keystroke = await mon.async_get()
                await keystroke_handler(connection, keystroke)
    # Run the monitor in the background
    asyncio.create_task(monitor())

    # Block regular handling of function keys
    filter = iterm2.KeystrokeFilter(connection, [pattern])
    async with filter as mon:
        await iterm2.async_wait_forever()

iterm2.run_forever(main)

集思广益

在给团队成员分享的过程中,队友们提出 sudo
可以明文设置管理员密码,不需要每次运行时手动输入,命令格式如下:

echo password | sudo -S xxxxxxxxx

需要注意的是,明文保存密码存在泄露的风险,建议使用这种方式前先评估风险。

使用该脚本,我们可以通过按下功能键来选择选项卡。F1
表示选择第一个选项卡,F2 表示选择第二个选项卡等。>>>
脚本下载

Apple Script 是什么?

AppleScript 是 Apple 创建的脚本语言,用于自动执行 Macintosh
操作系统及其许多应用程序操作的语言。可以用来控制运行于 macOS
上的程序,以及 macOS
本身的部分内容。你可以创建脚本来自动执行重复性任务,或组合多个可编写脚本的应用程序的功能来创建复杂的工作流程。

我们使用 AppleScript
创建备忘录、管理网络、处理图像、备份文件等等,AppleScript
是功能非常强大的自动化工具,它内置于 macOS 系统中,通过 脚本编辑器
应用来编写和运行,任何用户都可以免费使用它。

官方还提供了关于该功能的教程,包含了编写脚本的详细指南,并描述了
iTerm2 脚本系统的架构。

脚本编辑器

macOS 中默认已经提供编写 AppleScript
的工具:脚本编辑器,我们可以通过应用程序中找到并启动它。

澳门新萄京官方网站 2澳门新萄京官方网站,01.png

iTerm2 是 iTerm 的后继者,也是
Terminal 的替代者。这是一款用于 macOS
的终端模拟器,支持窗口分割、热键、搜索、自动补齐、无鼠标复制、历史粘贴、即时重播等功能特性,适用于
macOS 10.10 及以上版本。

我们第一个 AppleScript 脚本

我们打开脚本编辑器并选择新建文稿,输入以下内容:

tell application "Finder" to open the startup disk

点击运行后,系统会在桌面打开一个新的Finder窗口,显示启动盘的内容,这里我们已经完成了第一个
AppleScript 脚本。

再举个栗子,通过 AppleScript 打开或者关闭 Xcode:

tell application "Xcode" activate --quitend tell

从上面脚本可以看出,AppleScript
和自然语言非常接近,编写起来十分简单,但至于如何去学习 AppleScript
的语法,这里不会多做讲解,有兴趣的同学可以通过官方文档学习。

澳门新萄京官方网站 3

如何学习 AppleScript

澳门新萄京官方网站 403.png

Dash,程序员神器之一,强烈推荐一波。

我们在学习一门新的编程语言时,当在使用某个 API
遇到问题时,最靠谱的解决方法不是 Google,而是先看下它的文档
。我们可以通过 Dash 下载 AppleScript
文档,遇到问题时,我们可以快速查阅,并且 AppleScript
的文档并不多,空闲时可以通读一次,逐步加深对 AppleScript 的了解。

澳门新萄京官方网站 504.png

(文/开源中国)    

实践出真知

下面我们直接开始实操,来实现一个自动提醒发送重点项目进度的工具,除了提醒功能外,该工具还会自动帮你创建邮件模块,包括收件人、主题、内容模板等,我们来看实现脚本:

on callback() tell (current date) to get (its year as integer) & "-" & (its month as integer) & "-" & day set dataText to the result as text set mailTitle to "-重点项目进度" as text set mailTitle to dataText & mailTitle tell application "Microsoft Outlook" set newMessage to make new outgoing message with properties {subject:mailTitle, content:"Dear xx:<br/>以下是我的重点项目进度情况,详细请查看附件:<br/><br/><br/>Best Regards"} make new recipient at newMessage with properties {email address:{name:"xxx", address:"xxxx@pingan.com.cn"}} #make new cc recipient at newMessage with properties {email address:{name:"Name", address:"test@example.com"}} open newMessage end tellend callbackdisplay dialog "又到周一了,赶紧发重点项目进度" buttons {"现在发", "一会发"} default button 1if the button returned of the result is "现在发" then callback()end if

这里我们通过 AppleScript 访问 Microsoft Outlook
并自动生成邮件模板,以上脚本只给邮件内容填充了模块,但实际上还可以实现自动把你的重点项目进度excel统计表附件进来,再进一步的话,可以直接通过
AppleScript 读取 excel
的内容,然后填充到邮件内容中,只要你在完成重点项目内容后,及时更新 excel
统计表中的内容,那么到每周一发重点项目的时候,你只需要一个命令就可以自动完成汇报邮件。

我们完成了自动生成邮件模板的功能后,还需提供自动提醒的功能,关于自动提醒功能,我们可以使用
mac 的启动服务来实现,和自动配置双网上的方式一模一样,我们创建相应的
plist 文件:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>Label</key> <string>com.dengyonghao.viproject</string> <key>ProgramArguments</key> <array> <string>osascript</string> <string>/Users/dengyonghao/work/Tools/viproject.scpt</string> </array> <key>StartCalendarInterval</key> <dict> <key>Weekday</key> <integer>1</integer> <key>Minute</key> <integer>30</integer> <key>Hour</key> <integer>10</integer> </dict></dict></plist>

需要注意的是这里多了 StartCalendarInterval 这个 key,这个 key
是用来控制你脚本的执行周期或者时间的,这里设置为每周一上午 10:30
时就会自动执行脚本。

AppleScript 的内容就简单介绍到这里,总之 AppleScript
非常强大,具体怎么使用,建议空闲时共同探讨一下。

网站地图xml地图