Skip to content

Instantly share code, notes, and snippets.

@victorlin
Last active March 25, 2026 23:03
Show Gist options
  • Select an option

  • Save victorlin/6955cef5075560bffeb156d7e1d2d37f to your computer and use it in GitHub Desktop.

Select an option

Save victorlin/6955cef5075560bffeb156d7e1d2d37f to your computer and use it in GitHub Desktop.
helper script to copy extension files from one Chromium profile to another
#!/usr/bin/env bash
# one column for `select`
COLUMNS=1
main() {
local source_profile
local ext_id
local dest
local profiles=()
local other_profiles=()
mapfile -t profiles < <(find_profiles)
source_profile=$(choose_source_profile "${profiles[@]}")
echo "" >&2
ext_id=$(choose_extension "$source_profile/Extensions")
mapfile -t other_profiles < <(find_other_installations "$source_profile" "$ext_id" "${profiles[@]}")
echo ""
if [ ${#other_profiles[@]} -eq 0 ]; then
echo "No other profiles have this extension installed."
return 0
fi
dest=$(choose_dest "${other_profiles[@]}")
echo ""
generate_copy_commands "$source_profile" "$ext_id" "$dest"
}
find_profiles() {
local profiles=()
echo "Finding Chromium profiles…" >&2
echo "" >&2
mapfile -t profiles < <(find "$HOME/Library/Application Support/" -type d -name "Extensions" 2>/dev/null | while read -r ext_dir; do dirname "$ext_dir"; done)
if [ ${#profiles[@]} -eq 0 ]; then
echo "No Chromium profiles found." >&2
return 1
fi
printf '%s\n' "${profiles[@]}"
}
choose_source_profile() {
local profiles=("$@")
local profile_display
local display_dirs=()
for profile in "${profiles[@]}"; do
display_dirs+=("${profile#"$HOME/Library/Application Support/"}")
done
echo "Choose a profile to copy extension files from:" >&2
select profile_display in "${display_dirs[@]}"; do
if [ -n "$profile_display" ]; then
echo "You selected: $profile_display" >&2
printf '%s\n' "$HOME/Library/Application Support/$profile_display"
return 0
else
echo "Invalid selection." >&2
fi
done
}
choose_extension() {
local ext_dir="$1"
local ext_records=()
local ext_choice
echo "Loading extensions…" >&2
# list the extensions by ID and name (from manifest.json)
for id_dir in "$ext_dir"/*; do
if [ -d "$id_dir" ]; then
local ext_id
ext_id=$(basename "$id_dir")
# Extensions usually have a version subdirectory, such as 1.0.0_0/manifest.json
local manifest_file
manifest_file=$(find "$id_dir" -maxdepth 2 -name "manifest.json" | head -n 1)
if [ -n "$manifest_file" ] && [ -f "$manifest_file" ]; then
# Use jq to grab the best readable name without localized placeholders
local ext_name
ext_name=$(jq -r '[ .browser_action.default_title, .action.default_title, .name, .homepage_url, "Unknown Name" ] | map(select(type == "string" and (startswith("__MSG_") | not))) | .[0]' "$manifest_file" 2>/dev/null)
if [ -z "$ext_name" ] || [ "$ext_name" = "null" ]; then
ext_name="Unknown (unable to parse extension manifest)"
fi
ext_records+=("$ext_name"$'\t'"$ext_id")
fi
fi
done
# Sort extensions alphabetically by name (case-insensitive)
mapfile -t ext_records < <(printf '%s\n' "${ext_records[@]}" | sort -f)
local ext_names=()
local ext_ids=()
local ext_choices=()
for item in "${ext_records[@]}"; do
local ext_name="${item%$'\t'*}"
local ext_id="${item#*$'\t'}"
ext_names+=("$ext_name")
ext_ids+=("$ext_id")
ext_choices+=("$ext_name <https://chromewebstore.google.com/detail/$ext_id>")
done
echo "" >&2
echo "Choose an extension to copy files from:" >&2
select ext_choice in "${ext_choices[@]}"; do
if [ -n "$ext_choice" ]; then
for i in "${!ext_choices[@]}"; do
if [[ "${ext_choices[$i]}" = "$ext_choice" ]]; then
echo "You selected: ${ext_names[$i]}" >&2
printf '%s\n' "${ext_ids[$i]}"
return 0
fi
done
else
echo "Invalid selection." >&2
fi
done
}
find_other_installations() {
local source_profile="$1"
local ext_id="$2"
shift 2
local profiles=("$@")
local other_profiles=()
for profile in "${profiles[@]}"; do
local other_ext_dir="$profile/Extensions"
if [ "$profile" != "$source_profile" ] && [ -d "$other_ext_dir/$ext_id" ]; then
other_profiles+=("$profile")
fi
done
printf '%s\n' "${other_profiles[@]}"
}
choose_dest() {
local dest_profiles=("$@")
local display_profiles=()
for profile in "${dest_profiles[@]}"; do
display_profiles+=("${profile#"$HOME/Library/Application Support/"}")
done
echo "The extension is also installed in the following profiles." >&2
echo "" >&2
echo "Choose one to copy the extension files to:" >&2
select dest_display in "${display_profiles[@]}"; do
if [ -n "$dest_display" ]; then
local dest=""
for i in "${!display_profiles[@]}"; do
if [[ "${display_profiles[$i]}" = "$dest_display" ]]; then
dest="${dest_profiles[$i]}"
echo "You selected destination: $dest_display" >&2
printf '%s\n' "$dest"
return 0
fi
done
else
echo "Invalid selection." >&2
fi
done
}
generate_copy_commands() {
local source_profile="$1"
local ext_id="$2"
local dest="$3"
local source_ext_dir="$source_profile/Extensions"
echo "Run the following commands to copy the extension:"
echo ""
print_copy_command "$source_ext_dir/$ext_id" "$dest/Extensions/"
print_copy_command "$source_profile/Local Extension Settings/$ext_id" "$dest/Local Extension Settings/"
print_copy_command "$source_profile/IndexedDB/chrome-extension_${ext_id}_0.indexeddb.leveldb" "$dest/IndexedDB/"
print_copy_command "$source_profile/IndexedDB/chrome-extension_${ext_id}_0.indexeddb.blob" "$dest/IndexedDB/"
print_copy_command "$source_profile/Sync Extension Settings/$ext_id" "$dest/Sync Extension Settings/"
print_copy_command "$source_profile/Local App Settings/$ext_id" "$dest/Local App Settings/"
}
print_copy_command() {
local source_path="$1"
local dest_dir="$2"
if [ -d "$source_path" ]; then
echo "cp -R \"$source_path\" \"$dest_dir\""
elif [ -f "$source_path" ]; then
echo "cp \"$source_path\" \"$dest_dir\""
fi
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment