Skip to content
0

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
bash
brew install khard

Configuration 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 = no

Address 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 list
  • khard list -a addressbook1,addressbook2
  • khard show
  • khard show name_of_contact

Create contact

I have created three address books, work, family, and friends.

  • khard new pop up a selection menu for you to select address book and create a new contact
  • khard new -a work create the new contact in work address 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:
bash
khard list -a work
  • List contacts from both family and friends:
bash
khard list -a family,friends

2. 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:
bash
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:
bash
khard new -a private

4. 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:
bash
khard move -a work -A personal "John Smith"

When is it actually optional?

According to the documentation, -a is optional if:

  1. You only have one address book defined in your khard.conf.
  2. The search term is unique across all your address books.
  3. 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 steven
  • khard 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:

yaml
Address:
    home:
        Box:
        Extended: Apt 4567
        Street: 123 E Evelyn Ave
        Code: 94041
        City: San Francisco
        Region: CA
        Country: USA

The 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, USA

Standard 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, USA

UK address

Again, when run khard new or khard edit name_of_contact, the address information is collected using the fields below:

ymal
Address:
    home:
        Box:
        Extended:
        Street: Unit 5, Hounslow Business Park, Alice Way
        Code: TW3 3UD
        City: Middlesex
        Region:
        Country: United Kingdom

Typically, 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 Kingdom

Standard 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 Kingdom

There 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:

bash
#!/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:

bash
khard-address-us steven

Example output:

123 E Evelyn Ave
Apt 4567
San Francisco
CA 94041, USA
Steven Johnson
cell: +1 650-555-1234

It 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:

bash
khard postaddress --parsable steven

This 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	 home

You 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:

python
#!/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):

bash
# 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:

bash
khard-us steven
khard-uk john

Address Format Reference

CountryFormat
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

FeaturevCard 3.0 (RFC 2426)vCard 4.0 (RFC 6350)
Character EncodingAllows various (often defaults to ASCII/Latin-1).Strictly UTF-8. No more broken characters.
New PropertiesBasic info only.KIND (individual, group, org), GENDER, LANG.
Address (ADR)Includes dom, intl, postal, parcel.Simplified. These sub-types are removed.
Social MediaUses non-standard X- properties.Native support via IMPP or SOCIALPROFILE.
StandardizationSplit 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 PID and ALTID tags), which prevents the "data soup" that sometimes happens when importing 3.0 files.
  • Native Metadata: It supports ANNIVERSARY and GENDER natively, 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, khard allows you to export specifically to 3.0 using the --version flag: khard export --version 3.0 <contact>

Remain to be tested when I actually need such downgrade export! Period.

最近更新