#!/usr/bin/env bash # backup_chrome_bookmarks — copia byte a byte los archivos Bookmarks de perfiles # Chrome/Chromium a un directorio de backup con timestamp. Preserva el checksum # interno del archivo sin parsear ni reserializar el JSON. set -euo pipefail backup_chrome_bookmarks() { # ── defaults ────────────────────────────────────────────────────────────── local _user_data_dir="" local _profiles=() local _backup_dir="${HOME}/.local/share/web_scraping/bookmarks-backups" local _dry_run=0 # ── parse args ───────────────────────────────────────────────────────────── _usage() { cat >&2 <<'EOF' Usage: backup_chrome_bookmarks --user-data-dir [--profile ]... [--backup-dir ] [--dry-run] --user-data-dir (obligatorio) Raíz de perfiles de Chrome/Chromium. Ej: ~/.config/chromium-cdp --profile Nombre de carpeta de perfil a respaldar (repetible). Si no se pasa ninguno → respalda TODOS los perfiles con un archivo Bookmarks (excluye System Profile). --backup-dir Directorio raíz para backups. Default: ~/.local/share/web_scraping/bookmarks-backups --dry-run Muestra qué copiaría sin tocar nada. Exit codes: 0 éxito (o dry-run completado) 1 error de argumento o validación EOF return 1 } while [[ $# -gt 0 ]]; do case "$1" in --user-data-dir) _user_data_dir="$2"; shift 2 ;; --profile) _profiles+=("$2"); shift 2 ;; --backup-dir) _backup_dir="$2"; shift 2 ;; --dry-run) _dry_run=1; shift ;; -h|--help) _usage; return 0 ;; *) echo "backup_chrome_bookmarks: argumento desconocido: $1" >&2; return 1 ;; esac done # ── validaciones ────────────────────────────────────────────────────────── if [[ -z "$_user_data_dir" ]]; then echo "backup_chrome_bookmarks: --user-data-dir es obligatorio" >&2 return 1 fi if [[ ! -d "$_user_data_dir" ]]; then echo "backup_chrome_bookmarks: directorio no encontrado: ${_user_data_dir}" >&2 return 1 fi # ── descubrir perfiles si no se pasó ninguno ─────────────────────────────── if [[ ${#_profiles[@]} -eq 0 ]]; then local _candidate while IFS= read -r -d '' _candidate; do local _pname _pname="$(basename "$_candidate")" # Excluir System Profile (perfil interno de Chromium sin datos de usuario) if [[ "$_pname" == "System Profile" ]]; then continue fi if [[ -f "${_candidate}/Bookmarks" ]]; then _profiles+=("$_pname") fi done < <(find "$_user_data_dir" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) fi if [[ ${#_profiles[@]} -eq 0 ]]; then echo "backup_chrome_bookmarks: no se encontraron perfiles con archivo Bookmarks en: ${_user_data_dir}" >&2 return 1 fi # ── timestamp único para este backup ────────────────────────────────────── local _ts _ts="$(date +%Y%m%dT%H%M%S)" # ── procesar perfiles ───────────────────────────────────────────────────── # Construir el array de resultados JSON manualmente (sin jq ni python3) local _results="[" local _first=1 local _profile for _profile in "${_profiles[@]}"; do local _src="${_user_data_dir}/${_profile}/Bookmarks" # Si el perfil no tiene Bookmarks, se omite sin error if [[ ! -f "$_src" ]]; then continue fi local _dst="${_backup_dir}/${_ts}/${_profile}/Bookmarks" local _bytes _bytes="$(wc -c < "$_src")" if [[ $_dry_run -eq 1 ]]; then echo "backup_chrome_bookmarks: [dry-run] cp -p \"${_src}\" -> \"${_dst}\" (${_bytes} bytes)" >&2 else local _dst_dir _dst_dir="$(dirname "$_dst")" mkdir -p "$_dst_dir" cp -p "$_src" "$_dst" fi # Escapar comillas dobles en el path por si acaso local _src_esc="${_src//\"/\\\"}" local _dst_esc="${_dst//\"/\\\"}" local _profile_esc="${_profile//\"/\\\"}" local _entry _entry="$(printf '{"profile":"%s","src":"%s","dst":"%s","bytes":%s}' \ "$_profile_esc" "$_src_esc" "$_dst_esc" "$_bytes")" if [[ $_first -eq 1 ]]; then _results+="$_entry" _first=0 else _results+=",${_entry}" fi done _results+="]" # ── emitir resultado JSON ────────────────────────────────────────────────── local _backup_dir_esc="${_backup_dir//\"/\\\"}" printf '{"backup_dir":"%s","ts":"%s","profiles":%s}\n' \ "$_backup_dir_esc" "$_ts" "$_results" } # ── auto-ejecución ──────────────────────────────────────────────────────────── if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then backup_chrome_bookmarks "$@" fi