While launchctl is an essential tool for managing macOS services, its output is notoriously difficult for programs to parse. ldumpj is a command-line utility that tackles this challenge head-on by converting the human-readable output of launchctl into structured JSON. Internally, we parse the preprocessed output with a LALR grammer and transform the resulting AST into python objects.
Important Note: As
launchctlitself states, its output is not intended for programmatic parsing and is subject to change across macOS releases. This tool aims to provide a useful bridge for automation, but its stability is dependent on the consistency oflaunchctl's output format.
Automating tasks that involve launchctl dumpstate or launchctl print can be a headache if you wish to extrat complex information. Traditional scripting often relies on grep, which does the job for simple tasks. By converting to JSON, launchctl-dumpstate-json allows you to:
- Easily process
launchctldata with other scripting languages or tools. - Integrate
launchctlinformation into monitoring systems or dashboards. - Simplify complex automation workflows that depend on
launchctlstates.
For now, use uv to directly use the package, as you only need to use it likely once.
launchctl dumpstate | uvx git+https://github.com/bluuuk/launchctl-dumpstate-json Otherwise, use git clone https://github.com/bluuuk/launchctl-dumpstate-json and then use uv run ldumpj
usage: ldumpj [-h] [-i INPUT] [-o OUTPUT] [-p]
Parses the output of `launchctl dumpstate` and `launchctl print` into json
options:
-h, --help show this help message and exit
-i INPUT, --input INPUT
Input file, defaults to stdin
-o OUTPUT, --output OUTPUT
Output file, defaults to stdout
-p, --pretty JSON indentation for human-readable output
ldumpj supports both launchctl dumpstate (for all services) and launchctl print {target} (for a specific service).
Let's say you want to get detailed information about the system/com.apple.systemstats.analysis:
We go from
❯ launchctl print system/com.apple.systemstats.analysis
system/com.apple.systemstats.analysis = {
active count = 2
path = /System/Library/LaunchDaemons/com.apple.systemstats.analysis.plist
type = LaunchDaemon
state = running
program = /usr/sbin/systemstats
arguments = {
/usr/sbin/systemstats
--daemon
}
default environment = {
PATH => /usr/bin:/bin:/usr/sbin:/sbin
}
environment = {
MallocSpaceEfficient => 1
XPC_SERVICE_NAME => com.apple.systemstats.analysis
}
domain = system
minimum runtime = 60
base minimum runtime = 60
exit timeout = 5
runs = 1
pid = 103
immediate reason = speculative
forks = 1
execs = 1
initialized = 1
trampolined = 1
started suspended = 0
proxy started suspended = 0
last exit code = (never exited)
endpoints = {
"com.apple.systemstats.analysis" = {
port = 0x3803
active = 1
managed = 1
reset = 0
hide = 0
watching = 0
}
}
spawn type = adaptive (6)
jetsam priority = 90
jetsam memory limit (active, soft) = 150 MB
jetsam memory limit (inactive, soft) = 150 MB
jetsamproperties category = daemon
jetsam thread limit = 32
cpumon = default
exponential throttling grace limit = 10
probabilistic guard malloc policy = {
activation rate = 1/1000
sample rate = 1/0
}
properties = keepalive | runatload | supports transactions | inferred program | system service | exponential throttling | tle system
}to
❯ launchctl print "system/com.apple.systemstats.analysis" | uvx git+https://github.com/bluuuk/launchctl-dumpstate-json -p
{
"system/com.apple.systemstats.analysis": {
"active count": 2,
"path": "/System/Library/LaunchDaemons/com.apple.systemstats.analysis.plist",
"type": "LaunchDaemon",
"state": "running",
"program": "/usr/sbin/systemstats",
"arguments": [
"/usr/sbin/systemstats",
"--daemon"
],
"default environment": {
"PATH": "/usr/bin:/bin:/usr/sbin:/sbin"
},
"environment": {
"MallocSpaceEfficient": 1,
"XPC_SERVICE_NAME": "com.apple.systemstats.analysis"
},
"domain": "system",
"minimum runtime": 60,
"base minimum runtime": 60,
"exit timeout": 5,
"runs": 1,
"pid": 103,
"immediate reason": "speculative",
"forks": 1,
"execs": 1,
"initialized": 1,
"trampolined": 1,
"started suspended": 0,
"proxy started suspended": 0,
"last exit code": "(never exited)",
"endpoints": {
"com.apple.systemstats.analysis": {
"port": 14339,
"active": 1,
"managed": 1,
"reset": 0,
"hide": 0,
"watching": 0
}
},
"spawn type": "adaptive (6)",
"jetsam priority": 90,
"jetsam memory limit (active, soft)": "150 MB",
"jetsam memory limit (inactive, soft)": "150 MB",
"jetsamproperties category": "daemon",
"jetsam thread limit": 32,
"cpumon": "default",
"exponential throttling grace limit": 10,
"probabilistic guard malloc policy": {
"activation rate": "1/1000",
"sample rate": "1/0"
},
"properties": "keepalive | runatload | supports transactions | inferred program | system service | exponential throttling | tle system"
}
}The true power of JSON output shines when combined with tools like jq. For instance, to get a list of all loaded services:
❯ launchctl dumpstate | uv run ldumpj -p | jq 'keys'Here's how ldumpj performs on various test dumpstate inputs:
❯ for i in test/*; do time uv run ldumpj -i "$i" -o /dev/null ; done
uv run ldumpj -i "$i" -o /dev/null 1.56s user 0.04s system 99% cpu 1.609 total
uv run ldumpj -i "$i" -o /dev/null 1.04s user 0.02s system 99% cpu 1.073 total
uv run ldumpj -i "$i" -o /dev/null 1.38s user 0.03s system 99% cpu 1.415 totalHere are some features planned for future releases:
- Enhance performance and memory efficiency for very large
launchctl dumpstateoutputs. But for now, it should be fast enough - Improved resiliency: Further refine the parsing logic to handle unexpected variations in
launchctloutput more gracefully. - Improve packaging
License: MIT Lukas Boschanski