Parsing property lists (*.plist) with Python
Sometimes one needs to know state of a plist before changing settings. For example, when changing LSHandlers in LaunchServices.
We can see what’s inside the file using defaults read
command:
1
2
3
4
5
6
7
8
9
10
11
$ defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist
{
LSHandlers = (
{
LSHandlerPreferredVersions = {
LSHandlerRoleAll = "-";
};
LSHandlerRoleAll = "us.zoom.xos";
LSHandlerURLScheme = zoomphonecall;
},
...
Now we could use plists
, pure-python stdlib-only Python package to parse the binary plist file, but the output of defaults
looks so close to JSON that we could just convert it with just a few changes.
Let’s make plist.py
containing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
import json
import os
import re
import subprocess
import sys
def parse(path):
p = subprocess.run(['defaults', 'read', os.path.abspath(path)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if p.returncode:
raise NotImplementedError(p.stderr.decode())
o = p.stdout.decode()
o = re.sub(r' = ([^"]+);\n', ' = "\\1",\n', o)
o = re.sub(r'(\S+) =\s*', '"\\1": ', o)
o = re.sub(r':\s+\(', ': [', o)
o = re.sub(r';\n', ',\n', o)
o = re.sub(r'\)(,?)\n', ']\\1\n', o)
o = re.sub(r',(\s+[}\]])', '\\1', o)
return json.loads(o)
if __name__ == '__main__':
print(json.dumps(parse(sys.argv[1])))
Now we can run it:
1
2
$ python plist.py ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist
{"LSHandlers": [{"LSHandlerPreferredVersions":...
and pipe it to yq
:
1
python plist.py ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist | yq -P
to produce YAML:
1
2
3
4
5
6
LSHandlers:
- LSHandlerPreferredVersions:
LSHandlerRoleAll: '-'
LSHandlerRoleAll: us.zoom.xos
LSHandlerURLScheme: zoomphonecall
...
We can also make it available everywhere:
1
2
chmod +x plist.py
ln -s $(pwd)/plist.py /usr/local/bin/plist
and use it:
1
plist path-to-my.plist
Enjoy!
This post is licensed under CC BY 4.0 by the author.