Skip to content

Instantly share code, notes, and snippets.

@m-rey
Last active June 18, 2021 16:04
Show Gist options
  • Save m-rey/65c0656ccf5fcb859c0260e6798ef110 to your computer and use it in GitHub Desktop.
Save m-rey/65c0656ccf5fcb859c0260e6798ef110 to your computer and use it in GitHub Desktop.
function to bulk merge KeepassXC DBs (*.kdbx)
#!/bin/sh
# DESCRIPTION
# Merge one or more KeepassXc DBs into another KeepassXC DB.
#
# PURPOSE
# Syncthing can't natively merge/rebase KeepassXC DB files (yet?), thus it sometimes
# falls back to saving all differing DB versions as file sync conflicts.
# Luckily, `keepassxc-cli` *can* merge one DB into another, but only one at a time.
# As Syncthing often creates many conflict files, it would take ages to manually merge
# each DB. That's what `merge_kdbx` aims to solve:
# It's a wrapper for `keepassxc-cli` that implements merging many DBs into one.
#
# USAGE
# merge_kdbx target_db_file source_db_file [source_db_file2...]
#
# EXAMPLES
# merge_kdbx pw.kdbx pw.*conflict*.kdbx
# merge_kdbx pw.kdbx pw.*.kdbx <<< 'YOUR PASSWORD HERE'
# merge_kdbx pw.kdbx pw.*.kdbx <<< 'YOUR PASSWORD HERE' >merge.out.log 2>merge.err.log
#
# BACKUPS
# The script creates a backup dir for each bulk merge,
# containing a copy of the target DB before each individual merge.
# The generated backup dir is located in the same dir as the target DB, for ease of use.
#
# ASSUMPTIONS
# As the primary use case for this script is to merge almost identical DBs, some assumptions were made:
# - all DBs are secured using a password # TODO: Add support for keyfiles etc
# - all DBs share the same password # TODO: try to read pw from env var if set; TODO: add support for multiple pw?
# - your goal is to one-way-merge DB modifications back into the same DB. Once that's done, the other DBs can be deleted
# - you won't enter anything into the terminal, if it is still going through the `merge_kdbx` script. TODO: fix hacky pw input by using `expect` instead?
function merge_kdbx () {
if [ $# -lt 2 ]; then
echo "Error: Illegal number of passed databases. Please provide a merge target and at least one merge source."
exit 64
fi
target_db="$1"
shift
source_dbs=("$@")
printf 'Enter password of target db "%s":\n' "$target_db"
read -rs TARGET_DB_PASSWORD
# create a new time-stamped backup dir each time this *function* is executed to later save a backup after every individual merge in it
backup_dir_target_db="$(mktemp --directory --tmpdir="$(readlink -f "$target_db" | xargs dirname)" "$(date -u +"%Y-%m-%dT%H-%M-%S")_BACKUP_$(basename "$target_db"_XXXXXX)")"
for source_db in "${source_dbs[@]}"; do
# create empty, uniquely backup file
backup_target_db="$(mktemp --tmpdir="$backup_dir_target_db" "$(basename "$target_db")~before~$(basename "$source_db")~XXXXXX.bak")"
# copy db to empty backup file and print result
printf 'Creating backup: %s\n' "$(cp --verbose "$target_db" "$backup_target_db")"
printf 'Merging database "%s" into "%s":\n' "$source_db" "$target_db"
# it's expected that all databases use the same credentials, as they are most likely sync conflicts
keepassxc-cli merge --same-credentials "$target_db" "$source_db" <<<"$TARGET_DB_PASSWORD"
done
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment