khard
Khard is an address book for the Unix command line. It can read, create, modify and delete vCard address book entries. Khard only works with a local store of vCard files. It is intended to be used in conjunction with other programs like an email client, text editor, vdir synchronizer or VOIP client.
Installation
- macOS
- iTerm2
brew install khardConfiguration template
# example configuration file for khard version > 0.14.0
# place it under ~/.config/khard/khard.conf
# This file is parsed by the configobj library. The syntax is described at
# https://configobj.readthedocs.io/en/latest/configobj.html#the-config-file-format
[addressbooks]
[[family]]
path = ~/.contacts/family/
[[friends]]
path = ~/.contacts/friends/
[[work]]
path = ~/.contacts/work/
type = discover
[general]
debug = no
default_action = list
# These are either strings or comma seperated lists
# editor = vim, -i, NONE
editor = nvim
merge_editor = vimdiff
[contact table]
# display names by first or last name: first_name / last_name / formatted_name
display = first_name
# group by address book: yes / no
group_by_addressbook = no
# reverse table ordering: yes / no
reverse = no
# append nicknames to name column: yes / no
show_nicknames = no
# show uid table column: yes / no
show_uids = yes
# show kind table column: yes / no
show_kinds = no
# sort by first or last name: first_name / last_name / formatted_name
sort = last_name
# localize dates: yes / no
localize_dates = yes
# set a comma separated list of preferred phone number types in descending priority
# or nothing for non-filtered alphabetical order
preferred_phone_number_type = pref, cell, home
# set a comma separated list of preferred email address types in descending priority
# or nothing for non-filtered alphabetical order
preferred_email_address_type = pref, work, home
[vcard]
# extend contacts with your own private objects
# these objects are stored with a leading "X-" before the object name in the vcard files
# every object label may only contain letters, digits and the - character
# example:
# private_objects = Jabber, Skype, Twitter
# default: , (the empty list)
private_objects = Jabber, Skype, Twitter
# preferred vcard version: 3.0 / 4.0
preferred_version = 3.0
# Look into source vcf files to speed up search queries: yes / no
search_in_source_files = no
# skip unparsable vcard files: yes / no
skip_unparsable = noAddress book directories need to be created in advance, before you could reference them in the ~/.config/khard/khard.conf file. This is the only mandatory section as required inside the configuration file.
I don't have many legacy name cards and I don't have compatibility issues. So after a quick comparison on vCard 3.0 and 4.0, I decided to embrace the latest version 4.0.
Command line usage
Show contacts
khard listkhard list -a addressbook1,addressbook2khard showkhard show name_of_contact
Create contact
I have created three address books, work, family, and friends.
khard newpop up a selection menu for you to select address book and create a new contactkhard new -a workcreate the new contact inworkaddress book
Edit contact
khard edit [-a addr_name] [search terms [search terms ...]]
List email addresses
khard email -a work
The -a parameter
In the official khard documentation, the -a parameter (short for --addressbook) is used to explicitly define the scope of your command.
While khard is designed to be "smart"—searching all your configured address books by default to find a match—the -a flag becomes essential when you want to avoid ambiguity, speed up a search, or direct a new entry to a specific location.
Correct Usage & Scope
The -a flag filters which address books khard interacts with for a given subcommand. You can specify a single address book name or a comma-separated list of names (with no spaces).
1. Filtering Results (list, phone, email)
If you have multiple address books (e.g., work, family, friends) and only want to see entries from one or two of them, use -a.
- List only work contacts:
khard list -a work- List contacts from both family and friends:
khard list -a family,friends2. Resolving Ambiguity (show, edit, remove)
If you have a "John Smith" in both your work and personal address books, running khard show John Smith will force khard to ask you which one you mean. You can skip the prompt by being specific:
- Show the work version of a contact:
khard show -a work "John Smith"3. Targeted Creation (new, add-email)
This is arguably the most important use. If you have more than one address book configured, khard needs to know where to save a new contact.
- Create a new contact in the "private" book:
khard new -a private4. Moving or Copying Contacts
When moving or copying, -a defines the source address book, while -A (capital A) defines the target.
- Move a contact from work to personal:
khard move -a work -A personal "John Smith"When is it actually optional?
According to the documentation, -a is optional if:
- You only have one address book defined in your
khard.conf. - The search term is unique across all your address books.
- You want to search everywhere simultaneously (the default behavior).
TIP
If you find yourself typing -a work every single time, you can set a default address book for certain actions in your khard.conf, but the CLI flag will always override those defaults.
To quickly refresh your memory, run khard --help to show the help message and exit. For more information about the command line usage, check official resources below:
Custom address templates
khard show stevenkhard postaddress steven
Both commands print the address of Steven, suitable for printing onto an envelope. But the default address format only works nicely for some European countries, definitely not acceptable for the US, not for the UK, and probably not for China.
I will explain why this is the case with the following examples.
US address
When run command khard edit steven inside the terminal and edit contact Steven inside Neovim, the address information is collected via the following fields:
Address:
home:
Box:
Extended: Apt 4567
Street: 123 E Evelyn Ave
Code: 94041
City: San Francisco
Region: CA
Country: USAThe default address format is definitely non-American standard:
{Street}
{Extended}
{Code} {City}
{Region}, {Country}You can run command khard show name_of_contact to verify.
The format you are seeing (Code City) is the standard European or German convention, which khard defaults to as its primary developer is German.
Address
home:
123 E Evelyn Ave
Apt 4567
94041 San Francisco
CA, USAStandard US address format:
{Street}
{City}
{Region} {Code}, {Country}Following is an example of a standard US address. For US-style formatting (City Region Code), you will likely use shell scripts to re-format the default German style to an American one.
Address
home:
123 E Evelyn Ave
Apt 4567
San Francisco
CA 94041, USAUK address
Again, when run khard new or khard edit name_of_contact, the address information is collected using the fields below:
Address:
home:
Box:
Extended:
Street: Unit 5, Hounslow Business Park, Alice Way
Code: TW3 3UD
City: Middlesex
Region:
Country: United KingdomTypically, the Region filed is often left blank.
Current khard output format (non British standard):
Address
home:
Unit 5, Hounslow Business Park, Alice Way
TW3 3UD Middlesex
United KingdomStandard UK address format:
{Street}
{City}, {Code}
{Country}An example of a standard UK address:
Address
home:
Unit 5, Hounslow Business Park, Alice Way
Middlesex, TW3 3UD
United KingdomThere is not an established solution to this format problem, as custom address templates are currently unavailable for the users.
Workaround
Since khard does not support custom address templates natively, you can use the following workarounds to display addresses in US/UK standard formats.
Option 1: Use YAML output with a shell script
The khard show --format=yaml command outputs contact data in a structured YAML format that can be parsed and reformatted:
#!/usr/bin/env bash
# Filename: ~/.local/bin/khard-address-us
# ~/.local/bin/khard-address-us
# Format address in US standard
contact="$1"
data=$(khard show --format=yaml "$contact")
street=$(echo "$data" | grep -A20 "Address:" | grep "Street:" | sed 's/.*Street: *//')
extended=$(echo "$data" | grep -A20 "Address:" | grep "Extended:" | sed 's/.*Extended: *//' | grep -v "^$")
city=$(echo "$data" | grep -A20 "Address:" | grep "City:" | sed 's/.*City: *//')
region=$(echo "$data" | grep -A20 "Address:" | grep "Region:" | sed 's/.*Region: *//')
code=$(echo "$data" | grep -A20 "Address:" | grep "Code:" | sed "s/.*Code: *//" | sed "s/^'//;s/'$//")
country=$(echo "$data" | grep -A20 "Address:" | grep "Country:" | sed 's/.*Country: *//')
name=$(echo "$data" | grep "Formatted name:" | sed 's/.*Formatted name: *//')
phone=$(echo "$data" | grep -A5 "Phone:" | grep -m1 "cell\|mobile\|work" | head -1)
# Note:
# The sed step is intentionally omitted to preserve the type label
# (e.g., "cell:", "work:") in the output.
# This helps identify the phone type when printing envelopes or labels.
# The actual match depends on the order fields appear in the YAML output,
# not the order in your regex pattern.
# khard outputs phone types in a consistent order
# (typically: home, cell, work, etc.),
# This remains to be tested though, after I have a better understanding on vCard 4.0
# We always want cell to be extracted as the first priority.
echo "$street"
[ -n "$extended" ] && echo "$extended"
echo "$city"
echo "$region $code, $country"
echo "$name"
[ -n "$phone" ] && echo "$phone"Usage:
khard-address-us stevenExample output:
123 E Evelyn Ave
Apt 4567
San Francisco
CA 94041, USA
Steven Johnson
cell: +1 650-555-1234It worked nicely to print an address, with a contact and a phone number, and then you can copy this output elsewhere.
Option 2: Use --parsable flag with postaddress
For simpler scripting, use the --parsable flag which outputs tab-separated values:
khard postaddress --parsable stevenThis outputs: address<TAB>name<TAB>type
{'box': '', 'extended': 'Apt 4567', 'street': '123 E Evelyn Ave', 'code': '94041', 'city': 'San Francisco', 'region': 'CA', 'country': 'USA'} Steven Johnson homeYou can then pipe this through awk or other text processing tools to reformat.
Option 3: Use Python script for advanced formatting
For more robust parsing, use Python with the vobject or vcard library:
#!/usr/bin/env python3
import subprocess
import yaml
import sys
def get_contact_yaml(name):
result = subprocess.run(['khard', 'show', '--format=yaml', name],
capture_output=True, text=True)
return yaml.safe_load(result.stdout)
def format_us_address(contact):
addr = contact.get('Address', {}).get('home', {})
lines = []
lines.append(addr.get('Street', ''))
if addr.get('Extended'):
lines.append(addr.get('Extended'))
city = addr.get('City', '')
region = addr.get('Region', '')
code = addr.get('Code', '')
country = addr.get('Country', '')
lines.append(f"{city}, {region} {code}")
lines.append(country)
return '\n'.join(filter(None, lines))
if __name__ == '__main__':
contact = get_contact_yaml(sys.argv[1])
print(format_us_address(contact))Option 4: Create an alias for quick formatting
Add to your shell config (~/.zshrc or ~/.bashrc):
# US address format alias
alias khard-us='f() { khard show --format=yaml "$1" | python3 -c "
import sys, yaml
data = yaml.safe_load(sys.stdin)
a = data.get(\"Address\", {}).get(\"home\", {})
print(a.get(\"Street\", \"\"))
if a.get(\"Extended\"): print(a.get(\"Extended\"))
print(f\"{a.get(\"City\", \"\")}, {a.get(\"Region\", \"\")} {a.get(\"Code\", \"\")}\")
print(a.get(\"Country\", \"\"))
"; }; f'
# UK address format alias
alias khard-uk='f() { khard show --format=yaml "$1" | python3 -c "
import sys, yaml
data = yaml.safe_load(sys.stdin)
a = data.get(\"Address\", {}).get(\"home\", {})
print(a.get(\"Street\", \"\"))
if a.get(\"Extended\"): print(a.get(\"Extended\"))
print(f\"{a.get(\"City\", \"\")}, {a.get(\"Code\", \"\")}\")
print(a.get(\"Country\", \"\"))
"; }; f'Usage:
khard-us steven
khard-uk johnAddress Format Reference
| Country | Format |
|---|---|
| US | {Street}{Extended} (optional){City}{Region} {Code}, {Country} |
| UK | {Street}{Extended} (optional){City}, {Code}{Country} |
| DE (khard default) | {Street}{Extended}{Code} {City}{Region}, {Country} |
vCard
The choice between vCard 3.0 and 4.0 usually comes down to a trade-off between modern features and universal compatibility.
Key Differences at a Glance
| Feature | vCard 3.0 (RFC 2426) | vCard 4.0 (RFC 6350) |
|---|---|---|
| Character Encoding | Allows various (often defaults to ASCII/Latin-1). | Strictly UTF-8. No more broken characters. |
| New Properties | Basic info only. | KIND (individual, group, org), GENDER, LANG. |
Address (ADR) | Includes dom, intl, postal, parcel. | Simplified. These sub-types are removed. |
| Social Media | Uses non-standard X- properties. | Native support via IMPP or SOCIALPROFILE. |
| Standardization | Split across multiple RFC documents. | Consolidated into one clean standard. |
Why vCard 4.0 is Technically Superior
If you are building a modern workflow (like your Neovim + khard setup), vCard 4.0 is the technically correct choice for several reasons:
- Multilingual Support: Because UTF-8 is mandatory, you will never see "jumbled" text when dealing with international addresses or names.
- Property Grouping: It has a much more robust system for identifying which phone number belongs to which address (using
PIDandALTIDtags), which prevents the "data soup" that sometimes happens when importing 3.0 files. - Native Metadata: It supports
ANNIVERSARYandGENDERnatively, whereas 3.0 requires "hacks" (X-properties) that vary between every application (e.g., Apple vs. Google).
Why vCard 3.0 is Still Used
Despite being 15+ years old, 3.0 remains the "safe" default.
- Compatibility: Some legacy car Bluetooth systems, older Outlook versions, and older CRM software may fail to import 4.0 files entirely.
- Stability: Because it is so old, almost every parser on earth handles it predictably.
The Recommendation for Your Workflow
When I copied the default configuration file from the official site, preferred_version = 3.0 was the standard.
For my specific use case with NeoMutt, Neovim and khard, I recommend using vCard 4.0.
The Reason:khard and vdirsyncer (the tools typically used for CLI contact management) handle vCard 4.0 exceptionally well. Since I'm scripting my own address expansion, the strict UTF-8 requirement of 4.0 ensures that my bash scripts and Lua patterns won't break when they encounter special characters in names or street addresses.
But beware, that khard cannot convert already existing contacts from version 3.0 to 4.0. Therefore this setting is not applicable to the modify action.
That means, I will have to manually create vCard version 4.0 and delete vCard version 3.0, gradually. And most importantly, I don't have that many vCard 3.0 name cards after all.
Pro Tip: If you ever need to share a contact with someone using a very old device,
khardallows you to export specifically to 3.0 using the--versionflag:khard export --version 3.0 <contact>
Remain to be tested when I actually need such downgrade export! Period.