如何知道特定的launchd.plist文件位置?

34

是否有可能知道launchctl命令加载的.plist文件位置?

launchctl list会列出标签名称,launchctl list LABEL会显示其内容,但我找不到.plist文件的位置。

我知道它将位于/Library/LaunchAgent~/Library/LaunchAgent或其他地方,但我不想在所有通过launchctl命令列出的作业路径中搜索。


谢谢trojanfoe,我不知道这个网站! - Kurosawa Hiroyuki
8个回答

22

从macOS 10.12.6开始(之前版本不确定),可以使用launchctl dumpstate命令,获取有关所有正在运行进程的丰富信息。

在与该作业相关的信息的第一行中查找<LABEL> = {

以下是获取所有活动守护程序及其属性列表路径的单行命令:

grep -B 1 -A 4 "active count = 1$" <<< "$(launchctl dumpstate)"

更新:自10.12版本以来,macOS已添加了其他值,因此grep -A n已增加到4行。


在我的电脑上,这个不起作用。它只提供了部分转储,然后在随机服务的中途停止,没有任何错误消息或其他东西,而且恰好在传输20MB数据之后。 - user578895
@None:你所问的后续问题launchctl dumpstate无法提供完整转储 - smci
@JoelBruner:launchctl dumpstate非常有用;请注意,它是launchctl的一个未记录的子命令,甚至没有列在“LEGACY SUBCOMMANDS”下。 - smci

16

这个问题经常出现,不幸的是locatemdfind都不能显示我的系统上适当目录中的结果。 我将以下函数放入了我的.bashrc文件中,以便快速搜索launchctl查找plist文件的目录。

launchctlFind () {
    LaunchctlPATHS=( \
        ~/Library/LaunchAgents \
        /Library/LaunchAgents \
        /Library/LaunchDaemons \
        /System/Library/LaunchAgents \
        /System/Library/LaunchDaemons \
    )

    for curPATH in "${LaunchctlPATHS[@]}"
    do
        grep -r "$curPATH" -e "$1"
    done
    return 0;
}

需要注意的是,这只会检查在开机和登录时launchctl寻找文件的目录中是否存在。它可能无法找到所有内容,因为用户和/或其他进程可以手动加载作业。

更新:对于运行 macOS 10.12.6 或更高版本的用户,我建议使用 Joel Bruner 的解决方案。


最好将“grep -r”更改为“grep -R”,以便让grep命令跟随符号链接。 - wukong

4
launchctl list中使用的进程名称在plist中声明。虽然plist应该位于上述位置,但它们几乎可以放在任何地方。
我用' locate 找到了我要找的plist。我正在寻找org.postgresql.postgres,locate *.plist | grep org.postgresql.postgres将其缩小到4个文件。

4

在最近的macOS版本中,您可以使用launchctl print命令。您必须知道它正在运行的域,例如systemgui/<uid>。您可以在man页面中找到一些其他域,但到目前为止,我只看到了在这两个域下运行的服务。示例:

% launchctl print gui/$(id -u)/com.apple.cloudphotod  | grep path
    path = /System/Library/LaunchAgents/com.apple.cloudphotod.plist

 % launchctl print system/com.openssh.sshd | grep path 
    path = /System/Library/LaunchDaemons/ssh.plist
    stderr path = /dev/null

我相信这个命令是在High Sierra或其后版本中实现的。


4

这个问题可能没有答案!似乎不是 launchctl list 中的所有内容都有 plist 文件。

如上所述,launchctl dumpstate 将为您提供有关事物的大量信息,包括 plist 路径(如果存在)。

您可以运行此命令以大致查看正在运行的所有内容及其 plist 路径的列表。

launchctl dumpstate | grep -A4 " = {" | grep -B 3 -A 3 -E "active count = [1-9]"

(虽然这似乎也包括由launchd管理的守护程序以外运行的其他程序?)

代理/守护进程下面会有一个路径字段。通常,这些路径指向由Reed的答案描述的标准5个位置中的一个plist文件。但不一定是这样的。例如,Steam从非标准位置加载launchctl服务。

➜ launchctl dumpstate | grep -A4 " = {" | grep -B 3 -A 3 -E "active count = [1-9]"  | grep valve
com.valvesoftware.steam.ipctool = {
    path = /Users/chris/Library/Application Support/Steam/com.valvesoftware.steam.ipctool.plist

幸运的是,这种做法并不太流行,因此通常在标准位置搜索就足够了。

但这并不是最棘手的问题。我不知道具体情况,但似乎可以在没有相应plist文件的情况下加载launchctl服务。例如,这是dumpstate为1Password助手守护程序所说的内容。

➜ launchctl dumpstate | grep -A4 " = {" | grep -B 3 -A 3 -E "active count = [1-9]"  | grep -A4 "onepassword7-helper = {"
2BUA8C4S2C.com.agilebits.onepassword7-helper = {
    active count = 5
    path = (submitted by smd.1154)
    state = running

我不知道“submitted by smd”实际上是什么意思,但问题在于,即使我在launchctl list中看到一个辅助程序,可能在文件系统中没有任何plist。因此,我不知道如何使用launchctl unload来卸载此服务,因为卸载需要plist路径!由于这个进程是由launchd管理的,即使我使用pkill -9 onepassword7-helperlaunchd也会发现该进程停止并立即重新启动它。
(幸运的是,对于这个特定的1Password示例,如果在点击Quit 1Password时按住^,将出现一个特殊的“完全退出1Password”选项)

有趣的一面,我已经使用 grep 几年了,直到我刚刚读到这篇文章时才意识到 -B-A 标志。这非常有用。谢谢! - Geoff

3
以下是命令,用于列出所有已加载的.plist文件及其对应的文件:
``` find /System/Library/Launch* /Library/Launch* ~/Library/Launch* -name '*.plist' -exec sh -c '/usr/libexec/PlistBuddy -c "Print Label" {} && echo {}' ';' | grep -wf <(launchctl list | grep -o "\S\+\..*$") -A1 | strings ```
或者另一个版本:
``` find /System/Library/Launch* /Library/Launch* ~/Library/Launch* -name '*.plist' -exec /usr/libexec/PlistBuddy -c "Print Label" {} ';' -print | grep -wf <(launchctl list | grep -o "\S\+\..*$") -A1 | strings ```
解释如下:
- 在以下位置中查找所有的.plist文件:/System/Library/Launch*、/Library/Launch*、~/Library/Launch* - 使用PlistBuddy命令打印所有找到的.plist文件的标签 - 使用find的-print参数打印该文件的路径 - 获取另一个已加载到launchd中的作业列表,并将其用作grep -f的模式文件 - 过滤两个列表并找到共同元素,并打印其标签以及其路径(-A1) - 通过strings过滤以避免打印二进制文件

3
请注意,可以将未在任何默认位置中的plist加载到launchctl中。 Steam和1Password就是这样做的。 因此,这不会找到所有内容。 - Chris

2

由于launchctl list列出PID,一种方法是使用lsof命令查看进程加载的所有文件,例如:

launchctl list | grep -o '^[0-9]\+' | xargs -n1 lsof -p | grep plist$

另一种方法是运行fs_usage命令并重新加载.plist文件,例如:

sudo fs_usage | grep -w launchd | grep -w plist

1
在至少Catalina上,plist文件不会被启动的进程保持打开状态,因此这个解决方案行不通。非常遗憾。不过这是个好主意。 - Otheus

0
在Catalina上,我设计了这个脚本来打印出在“system”域或已登录用户中找到的所有正则表达式匹配对象的plist。请注意,这是一个BASH脚本,但在我的ZSH测试中也可以工作。
用法:find_loaded_plist [ regex ] 请在下面的评论中报告错误/建议。
find_loaded_plist() {
     launchctl list  | 
     awk "NR>1 && /${1:-^}/  { print \$3 }"  | 
     while read name; do
             launchctl print system/$name 2>/dev/null | 
                sed -n 's/^[[:space:]]*path = //p'
             for u in $(users |xargs id -u ) ; do                                 
                  launchctl print uid/$u/$name 2>/dev/null |
                     sed -n 's/^[[:space:]]*path = //p'
             done;
     done; 
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接