• Mac 开发工具信息的备份


    相信大部分程序员都对开发环境的工具都有一些特殊的执念(???),如果在自己不习惯的环境中工作完全无法开展,怎么这个工具没有那个字体难受,我本人就是,换了新的 Mac 电脑后如何快速恢复到之前的开发工具呢?

    开发工具包括 App 和命令行工具。用移动硬盘+时间机器自动备份当然能够完美地解决备份和恢复问题,不过接下来讨论的是一种用 Python 实现的“便宜的”方案。

    App 信息备份

    对于 App Store 安装的 App,通过 Apple ID 可以方便地重新下载,而对于非 App Store 安装的小众 App(如通过 Git Release)怎么快速恢复?通过 homebrew、gem 安装的依赖包和小工具呢?如果不是高频使用的 App,我们大多也记不住名字,只有到使用时才能想起它。下面介绍我使用 Python 编写程序备份 App 信息,并定期自动发送到邮件。

    • 实现思路
      Mac OS 的 /Applications 文件夹中,保存了从 App Store 下载的 App,我们其他途径得到的 .dmg 等安装包也会引导我们将 App 安装到 /Applications中,每一个 App 对应一个 .app 后缀名的文件夹,右键选择任一个 App -> 显示包内容打开文件夹,打开 Contents/Info.plist,该 Info.plist 记录了 App 的名字、开发者、Bundle ID、版本信息,我们备份了这些信息,在需要时 就能准确地在 Git 重新搜索到该 App。如下是 Python 实现
    class AppInfo():
        def __init__(self,name,version,bundleid,folder):
            self.name = name
            self.version = version
            self.bundleid = bundleid
            self.folder = folder
        def __str__(self):
            return "<td>{3}</td>
    	<td>{0}</td>
    	<td>{1}</td>
    	<td>{2}</td>".format(self.name,self.version,self.bundleid,self.folder)
    
    def application_list():
        app_folder = "/Applications"
        def pharse_plist(path_dir,path_name):
            plist = {}
            with open(path_dir,'rb') as rbf:
                plist = plistlib.load(rbf)
            version = plist.get('CFBundleShortVersionString','-')
            bundleid = plist.get('CFBundleIdentifier','-')
            name = plist.get('CFBundleName','-')
            return AppInfo(name=name,version=version,bundleid=bundleid,folder=path_name)
        def list_in_dir(dir,level=0):
            dirs = os.listdir(dir) if os.path.isdir(dir) else []
            apps = []
            level -= 1
            for app in dirs:
                pre_path = os.path.join(dir,app)
                info_plist_path = os.path.join(pre_path,'Contents/Info.plist')
                if os.path.isfile(info_plist_path):
                    apps.append(pharse_plist(info_plist_path,app)) 
                elif level >= 0:
                    apps = apps + list_in_dir(pre_path,level=level)
            return apps
        app_str = ''
        for app in list_in_dir(app_folder,level=2):
            app_str += ('<tr>' + str(app) + '</tr>
    ')
        table_define = """<table frame='hsides'>
    {}{}'</table>'""".format('<tr>
    <th align="left">App</th>
    <th align="left">名字</th>
    <th align="left">版本</th>
    <th align="left">BundleID</th>
    </tr>',app_str)
        return table_define
    
    • 主要是遍历了 /Applications 文件夹,解析每个 App 的 Info.plist 文件,得到 App 列表信息,并加上了 html 标签进行格式化。注意 /Applications 里面可能包含文件夹,所以 list_in_dir 包含两层遍历

    命令行程序备份

    Mac 上的命令行工具大部分是从 homebrewrubygems 两个地方安装,接下来的 Python 代码演示备份此三处的命令行信息:

    def exe_command(list):
        result = subprocess.run(list,stdout=subprocess.PIPE)
        return result.stdout.decode("utf-8").strip('
    ')
    
    def gem_list():
        return exe_command(['/Users/$(whoami)/.rvm/rubies/ruby-2.4.1/bin/gem','list']).replace("\n",'<br>
    ')
    
    def brew_list():
        return exe_command(['/usr/local/bin/brew','list']).replace("\n",'<br>
    ')
    

    使用 subprocess.run 执行 shell 命令并得到标准输出,然后对输出做格式化处理,方便后续做阅读

    发送邮件

    上面得到了 App 和工具信息,现在将其发到邮箱保存。下面的代码使用了内置的邮件服务器,可能会被当做垃圾邮件或有风险的邮件而被拒收

    def temp_attachment_path():
        file_p = exe_command(['mktemp'])
        html_ext = file_p + '.html'
        os.rename(file_p,'file_p' + '.html')
        # MARK: 打印临时 html 的目录,可以预览发送的格式
        print(html_ext)
        return html_ext
    
    def sendEmail(html_content):
        msg = MIMEText(html_content,'html','utf-8')
        msg['From'] = 'APP_BACKUP@localhost.com'
        # TODO: 改成自己的邮箱地址
        msg['To'] = ' xxx@xxx.xxx' 
        msg['Subject'] = "XXXXX's MBP App 列表"
        p = Popen(["/usr/sbin/sendmail", "-t","-oi"], stdin=PIPE)
        p.communicate(msg.as_bytes())
    
    if __name__ == '__main__':
        app_str = application_list()
        pip3_str = '<br><h2>Pip3 Apps</h2><p>%s</p>'%pip3_list()
        gem_str = '<br><h2>Gem Apps</h2><p>%s</p>'%gem_list()
        brew_str = '<br><h2>Homebrew Apps</h2><p>%s</p>'%brew_list()
        content_str = app_str + pip3_str + gem_str + brew_str
        attachment_path = temp_attachment_path()
        with open(attachment_path,'w') as wf:
            wf.write(content_str)
        sendEmail(content_str)
    

    上述邮件发送的内容示例:

  • 相关阅读:
    WPF(ContentControl和ItemsControl)
    WPF(x:Key 使用)
    WPF(Binding集合对象数据源)
    WPF(x:Type的使用)
    WPF(初识DataTemplate)
    Asp.net 全局错误处理
    给年轻程序员的建议(转自csdn)
    在.net中未能用trycatch捕获到的异常处理(转载)
    c#语音读取文字
    IIS 7.0 和 IIS 7.5 中的 HTTP 状态代码
  • 原文地址:https://www.cnblogs.com/boch2436/p/12629519.html
Copyright © 2020-2023  润新知