效果图在这里
解决办法
首先 你要有以下几个工具或者库
- python 安装方法 brew install python(brew怎么没这个命令…没有的同学请自行百度)ps:其实mac自带的python足够用了 我们只运行一个脚本而已
- pypinyin 安装方法 pip install pypinyin
- pyobjc 安装方法 pip install pyobjc
准备好环境之后 创建以下脚本
#! /usr/bin/python
# -*- coding: utf-8 -*-
# @author weishu @2015/12/7
import subprocess
import os
import re
import json
from AppKit import NSWorkspace, NSBundle
from pypinyin import lazy_pinyin
# copy from Alfred 2 preferences, if you have applications installed at other place, add it here.
APP_DIRECTORYS = [
u'/Applications',
u'/Applications/Xcode.app/Contents/Applications',
u'/Developer/Applications',
u'/Library/PreferencePannes',
u'/opt/homebrew-cask/Caskroom',
u'/System/Library/PreferencePannes',
u'/usr/local/Cellar',
u'~/Library/Caches/Metadata',
u'~/Library/Mobile Documents',
u'~/Library/PreferencePannes'
]
def _is_application(workspace, abspath):
''' is a `abspath` stand for a mac app'''
return workspace.isFilePackageAtPath_(abspath)
def _get_app_pinyin_name(app_name):
return reduce(lambda x, y: x + y, lazy_pinyin(app_name, errors='ignore'))
def _add_meta_data(app_pinyin_name, app_path):
''' add meta data(comments) to the app, which can help Alfred or SpotLight find it'''
print "processing %s" % app_path
try:
subprocess.check_call('xattr -w com.apple.metadata:kMDItemFinderComment %s "%s"' % (app_pinyin_name, app_path), shell=True)
except:
print "process %s failed, ignore." % app_path
def _get_localized_name(abs_path):
'''get the localized name of given app'''
bundle = NSBundle.new()
bundle.initWithPath_(abs_path)
localizations = bundle.localizations()
chinese = ('zh_CN', 'zh_Hans', 'zh-Hans', 'zh-CN')
b = any(map(lambda x: x in localizations, chinese))
if not b: return
for ch in chinese:
path = bundle.pathForResource_ofType_inDirectory_forLanguage_('InfoPlist', 'strings', None, ch)
if not path: continue
# the path must surround with "", there may be space characters
json_str = subprocess.check_output(u'plutil -convert json -o - "%s"' % path, shell=True)
# print json_str
json_res = json.loads(json_str, encoding='utf8')
name = json_res.get('CFBundleName')
if name: return name
def main():
pattern = re.compile(r'^[\w\s.]+$')
workspace = NSWorkspace.sharedWorkspace()
for app_dir in APP_DIRECTORYS:
if not os.path.exists(app_dir): continue
for root, dirs, files in os.walk(app_dir, topdown=True):
remove_list = []
for directory in dirs:
full_path = os.path.join(root, directory)
# print full_path
if not _is_application(workspace, full_path): continue
remove_list.append(directory)
try:
localized_name = _get_localized_name(full_path)
except:
print "get localized name for %s failed. ignore" % full_path
# continue
# print "localized_name:", localized_name if localized_name else None
app_name = localized_name if localized_name else directory.rsplit(r'.')[0]
if pattern.match(app_name):
# print "app_name: %s not match" % app_name
continue
try:
_add_meta_data(_get_app_pinyin_name(app_name), full_path)
except:
# may be empty, just ignore
print "add meta for %s failed" % full_path
# if this directory is already a Application
# do not traverse this; some app may be very large
# and there won't be any other app inside it
dirs[:] = [d for d in dirs if d not in remove_list]
if __name__ == '__main__':
main()
执行脚本
sudo python alfred-pinyin.py
that’s all
原理
Alfred检索程序不仅仅是检索名字,还收集了一些额外的信息;它利用了Mac文件系统的一个拓展信息的字段;如果你发现某些目录后面有@那么就是有拓展信息了:
drwxr-xr-x+ 3 root wheel 102 9 10 2014 Stickies.app/
drwxr-xr-x@ 3 weishu admin 102 3 26 2015 Sublime Text.app/
可以借助命令行工具xattr进行操作;具体使用可以man xattr.
所以,我们可以通过把拼音信息添加到文件的拓展信息里面去,这样Alfred就能借助这些信息帮助拼音检索了。
实现
获取程序名
程序名不仅仅是一个文件名这么简单,Mac软件有一个叫做localization的概念,大致就是国际化吧;程序结构把国际化的字段存放在不同的文件里面,在程序本地化之后自动load.
我们要使用的那个字段是CFBundleName
;存放在/<App>/Contents/Resources/<language>/InfoPlist.strings
这个文件里面;我们把这个名字读取出来即可。
尝试过使用objc
的接口NSBundle.localizedInfoDiction
来获取本地化的字段,无奈拿到的永远是英文字段;只好手工解析中文字段了(不会Objc );使用的命令行工具plutil:
def _get_localized_name(abs_path):
'''get the localized name of given app'''
bundle = NSBundle.new()
bundle.initWithPath_(abs_path)
localizations = bundle.localizations()
chinese = ('zh_CN', 'zh_Hans')
b = any(map(lambda x: x in localizations, chinese))
if not b: return
for ch in chinese:
path = bundle.pathForResource_ofType_inDirectory_forLanguage_('InfoPlist', 'strings', None, ch)
if not path: continue
# the path must surround with "", there may be space characters
json_str = subprocess.check_output(u'plutil -convert json -o - "%s"' % path, shell=True)
# print json_str
json_res = json.loads(json_str, encoding='utf8')
name = json_res.get('CFBundleName')
if name: return name
转换为拼音
可以直接使用python的拼音转换库pypinyin,借助这个工具,一行代码搞定:
def _get_app_pinyin_name(app_name):
reduce(lambda x, y: x + y, lazy_pinyin(app_name, errors='ignore'))
添加拼音信息
拼音信息被添加到文件的拓展信息里面,直接使用xattr添加即可:
def _add_meta_data(app_pinyin_name, app_path):
''' add meta data(comments) to the app, which can help Alfred or SpotLight find it'''
subprocess.check_call('xattr -w com.apple.metadata:kMDItemFinderComment %s %s' % (app_pinyin_name, app_path), shell=True)
好了,把这些代码整合起来,就能得到最终的结果了.
def main():
pattern = re.compile(r'^[\w\s.]+$')
workspace = NSWorkspace.sharedWorkspace()
for app_dir in APP_DIRECTORYS:
if not os.path.exists(app_dir): continue
for root, dirs, files in os.walk(app_dir, topdown=True):
remove_list = []
for directory in dirs:
# print type(directory), root, directory
full_path = os.path.join(root, directory)
if not _is_application(workspace, full_path): continue
remove_list.append(directory)
localized_name = _get_localized_name(full_path)
app_name = localized_name if localized_name else directory.rsplit(r'.')[0]
if pattern.match(app_name):
continue
_add_meta_data(_get_app_pinyin_name(app_name), full_path)
# if this directory is already a Application
# do not traverse this; some app may be very large
# and there won't be any other app inside it
dirs[:] = [d for d in dirs if d not in remove_list]
git 地址:https://gist.github.com/tiann/35fb758c18036d7f8640
参考文章:http://www.jianshu.com/p/dee4d6a0ed4f