<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Shell on Chen</title><link>https://hechen.github.io/zh-cn/tags/shell/</link><description>Recent content in Shell on Chen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 07 Sep 2026 12:00:00 -0700</lastBuildDate><atom:link href="https://hechen.github.io/zh-cn/tags/shell/index.xml" rel="self" type="application/rss+xml"/><item><title>如何获取 Mac 应用的 Bundle ID</title><link>https://hechen.github.io/zh-cn/post/reveal-bundleid-application/</link><pubDate>Mon, 10 Aug 2020 12:05:54 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/reveal-bundleid-application/</guid><description>&lt;p&gt;应用名称和 Bundle ID 是两回事。例如，App Store 的 Bundle ID 是 &lt;code&gt;com.apple.AppStore&lt;/code&gt;。脚本或配置描述文件要求填写应用标识符时，需要的就是这个值。&lt;/p&gt;
&lt;p&gt;它保存在应用的 &lt;code&gt;Info.plist&lt;/code&gt; 中，对应的键是 &lt;code&gt;CFBundleIdentifier&lt;/code&gt;。可以在 Finder 里查看，也可以用 macOS 自带的命令直接读取，不需要启动应用。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;2026 年 9 月 7 日更新：更换了截图，并在 macOS 27 测试版上验证了下方命令。Finder 的操作步骤也与 Apple 当前的部署指南一致。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="在-finder-中查看"&gt;在 Finder 中查看&lt;/h2&gt;
&lt;p&gt;找到应用，按住 Control 点击它，选择&lt;strong&gt;显示包内容（Show Package Contents）&lt;/strong&gt;，再打开 &lt;strong&gt;Contents&lt;/strong&gt; 文件夹，找到 &lt;strong&gt;Info.plist&lt;/strong&gt;。这也是 &lt;a href="https://support.apple.com/guide/deployment/get-the-bundle-id-for-a-mac-app-dep0af2cd611/web"&gt;Apple 文档介绍的方法&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Finder 中选中了 App Store.app 内的 Info.plist，底部显示了应用包路径。" loading="lazy" src="https://hechen.github.io/images/posts/reveal-bundleid-application/finder-info-plist.png"&gt;&lt;/p&gt;
&lt;p&gt;这台 Mac 上，App Store 的 plist 路径是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/System/Applications/App Store.app/Contents/Info.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下载的应用通常在 &lt;code&gt;/Applications&lt;/code&gt;，Apple 自带应用也可能位于 &lt;code&gt;/System/Applications&lt;/code&gt;。以实际安装位置为准，不要直接套用其他应用的路径。&lt;/p&gt;
&lt;p&gt;用文本编辑打开 plist，搜索 &lt;code&gt;CFBundleIdentifier&lt;/code&gt;。如果安装了 Xcode，也可以使用它的属性列表编辑器；界面中这个键可能显示为 &lt;strong&gt;Bundle identifier&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="文本编辑在 App Store 的 Info.plist 中找到了 CFBundleIdentifier，下一行的值为 com.apple.AppStore。" loading="lazy" src="https://hechen.github.io/images/posts/reveal-bundleid-application/textedit-bundle-identifier.png"&gt;&lt;/p&gt;</description></item><item><title>LetsMove 中的几个点</title><link>https://hechen.github.io/zh-cn/post/letsmove/</link><pubDate>Thu, 04 Jul 2019 01:19:10 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/letsmove/</guid><description>&lt;p&gt;当我们从网络上下载某个 mac app 之后，一般都会默认在 Downloads 目录里，但是某一些 App 是需要放置到 Application 目录才行，最常见的就是自更新功能是受到当前所处目录的限制的，这时候很多下载到本地的 .app 文件打开之后都会弹出提示框，让用户选择是否移动到 Applications 目录，LetsMove 就是大部分 mac app 参考或者集成的开源库，封装了这套逻辑。&lt;/p&gt;
&lt;p&gt;&lt;img alt="LetsMove" loading="lazy" src="https://i.imgur.com/99z1BQf.png"&gt;&lt;/p&gt;
&lt;p&gt;这个库已经好多年了，最近的一次 Commit 是 2017年，其中有几个点可以学习借鉴。&lt;/p&gt;
&lt;h3 id="大致思路"&gt;大致思路&lt;/h3&gt;
&lt;p&gt;虽然叫 Move，实际上是先行 Copy 一份当前的 App Bundle 到 Application 目录，然后杀掉自己并删除源目录的 App 文件。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move to Applications Folder 确认之后&lt;/li&gt;
&lt;li&gt;复制当前 App 文件到 /Applications 目录中&lt;/li&gt;
&lt;li&gt;删除当前已经打开 App （Mac 上是可行的，不同于 Windows，不存在占用文件句柄的情况）&lt;/li&gt;
&lt;li&gt;执行子进程执行重启过程，具体后文会讲。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="权限获取提权"&gt;权限获取（提权）&lt;/h3&gt;
&lt;p&gt;检查文件路径是否可以写入的方法是 NSFileManager 的 isWritableFileAtPath ，如果返回 false，LetsMove 会主动进行权限获取。&lt;/p&gt;
&lt;p&gt;获取权限所使用的接口是 AuthorizationExecuteWithPrivileges，该接口实际在 OSX 10.7 Lion 之后就已经标记废弃了，但是一直到目前的 macOS 10.14 还一直可以使用。&lt;/p&gt;
&lt;p&gt;而作者为了避免苹果直接移除该代码导致提权接口不可用，使用了动态查找方法的形式，利用 dlsym 查找方法指针来获取，从而避免如果完全移除该方法导致功能不稳定的情况发生。&lt;/p&gt;</description></item></channel></rss>