需求场景
我选中一个目录下的多个文件,然后对这一批文件进行相同或类似的操作。比如我选中几个md文件,然后对所有文件中的图片地址进行更新,这样就可以把本地保存的图片文件转移到其他目录,甚至换成图床的地址。
思路描述
- 在Path Finder中选择一个或多个文件;
- 调用KM(Keyboard Maestro)宏,取出所选文件的全路径列表;
- 遍历上一步得到的列表,调用shell依次处理每一个文件;
获取选中文件的路径
原生KM提供了一个For Each Path in Finder Selection的Action,只支持对Finder中选中的文件进行操作。因为我基本使用Path Finder进行日常操作,所以我需要实现一个PF版本的类似操作。考虑到实现一个Action的门槛较高,所以暂时选择使用Macro来实现。原来是打算使用AppleScript来实现,不过最近看到MacOS提供了JXA模式,用JavaScript来操作OS级别的任务。所以尽量使用JXA来实现我的需求。
基础版
仅支持单一目录下的文件
KM提供了Execute JavaScript for Automation的Action,在代码框中输入下面的代码。或者把代码保存为.js文件,在Action中直接调用文件。
// 获取每个文件的地址
function pathFolderFile(x) {
var strPath = x.posixPath();
return strPath;
}
// 获取Path Finder中选中的文件地址,如果是多个文件,就用\n符号进行分割
function run() {
var pfSeln = Application("Path Finder").selection();
var selectionList = pfSeln ? pfSeln.map(pathFolderFile) : [];
return selectionList.join("\n");
}
复制代码
上面的代码整体上很简单,几个重点如下:
- 如果没有选中任何文件,就返回空列表,避免后续操作出错。
- 最终需要把多个path结果返回到一个变量中供下一步操作,但是我目前使用的KM7没有提供List类型的变量,所以我把List的结果转换为一个用换行符分隔的字符串。
- Action中一定要设置 save variable to ,把JXA返回的结果存在变量中。
高级版
增加对文件夹的递归操作。
暂时没有找到JXA的处理方式,待续。。。
依次处理返回的文件列表
KM提供了For Each这个Action,对刚才返回的字符串可以根据换行符进行逐行操作。这个和python等语言里面的 for X in Y 结构是一样的,很容易理解。如下图
调用Shell脚本的坑
KM中的Execute a Shell Script操作,使用的是非交互式模式。即,当在KM中调用shell的时候,不会使用当前用户的环境变量。这和通过crontab调用shell的场景是一模一样的。因为最开始忽略这个细节,所以出现两个问题
- 调用python的时候,用了系统默认的2.7版,而不是3.5版。
- 在处理UTF-8字符集的时候报错,因为没有设置 export LANG=zh_CN.UTF-8。
解决这个问题的办法也很简单,和crontab运行代码一个套路,只需要在执行shell之前先运行当前用户的环境配置文件。如下
# 设置环境变量
source ${HOME}/.bash_profile
复制代码
完整的KM Macro
这里把JXA保存为.js文件进行调用