Managing Default File Associations with duti
Managing Default File Associations with duti
duti is a macOS command-line tool for setting default applications for file types, URL schemes, and MIME types. It's managed by Homebrew and configured via chezmoi for consistent cross-machine behavior.
Why Use duti
Problem: macOS "Open With" settings are:
- Stored in binary plist files (hard to version control)
- Reset unpredictably by macOS updates
- Tedious to configure manually across machines
- No scriptable interface
Solution: duti provides:
- ✅ Command-line control over default applications
- ✅ Scriptable configuration (version-controlled)
- ✅ Idempotent application (safe to run multiple times)
- ✅ Automated setup via chezmoi
Installation
duti is installed via Homebrew and configured automatically by chezmoi:
bashbrew install duti
Chezmoi integration: ~/.local/share/chezmoi/run_onchange_50-duti-defaults.sh runs automatically on chezmoi apply.
Current Configuration
Your duti configuration sets:
VS Code as default for coding file types:
- JavaScript/TypeScript:
.js,.ts,.jsx,.tsx,.mjs - System languages:
.c,.cpp,.h,.java,.py,.rb,.go,.rs,.swift,.kt - Shell scripts:
.sh,.zsh,.nu,.fish,.zshrc,.bashrc - Web/markup:
.html,.css,.scss,.json,.yaml,.yml,.md,.xml - Config files:
.toml,.ini,.cfg,.conf,.txt - LaTeX:
.tex,.cls,.sty,.bib,.bst,.aux,.log - Build files:
Makefile,Dockerfile,.cmake,.gradle
VLC as default for media files:
- Video:
.mp4,.avi,.mkv,.mov,.wmv,.flv,.webm,.m4v - Audio:
.mp3,.wav,.flac,.aac,.ogg,.m4a,.wma,.opus - Subtitles:
.srt,.vtt,.ass,.ssa,.sub,.idx
How It Works
Bundle Identifiers: duti uses macOS bundle IDs to identify applications:
| Application | Bundle ID |
|---|---|
| VS Code | com.microsoft.VSCode |
| VLC | org.videolan.vlc |
| Neovim (if needed) | io.neovim.neovim |
| Safari | com.apple.Safari |
| Chrome | com.google.Chrome |
Find an app's bundle ID:
bashosascript -e 'id of app "Visual Studio Code"' # Returns: com.microsoft.VSCode
Basic Usage
Set default application for a file extension:
bashduti -s com.microsoft.VSCode .py all
Set default for URL scheme:
bashduti -s com.google.Chrome http all duti -s com.google.Chrome https all
Check current default for extension:
bashduti -x py # Shows: VS Code (com.microsoft.VSCode)
List all configured defaults:
bashduti -l
Editing Your Configuration
Your duti defaults are managed by chezmoi at:
~/.local/share/chezmoi/run_onchange_50-duti-defaults.sh
Edit workflow:
bash# 1. Edit the script chezmoi edit ~/.local/share/chezmoi/run_onchange_50-duti-defaults.sh # 2. Add file extensions to arrays: CODING_EXTENSIONS=( # ... existing extensions lua vim # Add new extensions ) MEDIA_EXTENSIONS=( # ... existing extensions gif webp # Add image formats ) # 3. Apply changes chezmoi apply # 4. Commit to Git cd ~/.local/share/chezmoi git add run_onchange_50-duti-defaults.sh git commit -m "Add lua and vim to VS Code defaults" git push
Common Customizations
Add Neovim as default for specific files:
bash# In run_onchange_50-duti-defaults.sh NVIM_BUNDLE_ID="io.neovim.neovim" NVIM_EXTENSIONS=( zsh sh bash fish nu vimrc nvimrc ) for ext in "${NVIM_EXTENSIONS[@]}"; do duti -s "$NVIM_BUNDLE_ID" ".$ext" all done
Set browser for URL schemes:
bash# Add to run_onchange_50-duti-defaults.sh CHROME_BUNDLE_ID="com.google.Chrome" duti -s "$CHROME_BUNDLE_ID" http all duti -s "$CHROME_BUNDLE_ID" https all
Set different apps for specific extensions:
bash# PDFs to Preview duti -s com.apple.Preview .pdf all # Images to Preview duti -s com.apple.Preview .png all duti -s com.apple.Preview .jpg all
Troubleshooting
Changes not applying:
bash# Force re-run chezmoi script chezmoi apply --force # Or run script directly bash ~/.local/share/chezmoi/run_onchange_50-duti-defaults.sh
Application not found:
- Verify app is installed
- Check bundle ID:
osascript -e 'id of app "AppName"' - Some apps don't support file associations
Finder still shows wrong default:
- Right-click file → Get Info → Open With: → Change All...
- Or reboot (macOS caches Launch Services database)
Reset Launch Services database (nuclear option):
bash/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user killall Finder
Integration with Chezmoi
Why run_onchange_ prefix?
- Script runs automatically when its content changes
- Idempotent (safe to run multiple times)
- No manual intervention needed
- Part of automated machine setup
When it runs:
- On
chezmoi applyafter script changes - On new machine setup (
chezmoi init) - Never runs if script hasn't changed (efficient)
See also: run_onchange_40-asdf-tools.sh, run_onchange_60-python-packages.sh
Advanced Usage
Export current defaults to file:
bashduti > ~/my-defaults.duti
Import defaults from file:
bashduti ~/my-defaults.duti
Set defaults per user vs system-wide:
bashduti -s com.microsoft.VSCode .py all # Current user duti -s com.microsoft.VSCode .py system # System-wide (requires sudo)
Related Tools
| Tool | Purpose |
|---|---|
duti | Set default apps (what we use) |
defaults | macOS preferences system |
open -a | Open file with specific app once |
qlmanage | Quick Look management |
Links
duti GitHub Repository
- URL: https://github.com/moretension/duti
- Summary: Official duti repository with documentation and examples
- Related: chezmoi, Homebrew
macOS Launch Services
- URL: https://developer.apple.com/documentation/coreservices/launch_services
- Summary: Apple's documentation on Launch Services (what duti configures)
- Related: macOS