#!/usr/bin/env bash # restore_chrome_bookmarks — restaura archivos Bookmarks de un backup generado por # backup_chrome_bookmarks hacia los perfiles destino en user-data-dir. # Copia byte a byte con cp -p (nunca parsea ni reserializa el JSON). set -euo pipefail restore_chrome_bookmarks() { # ── defaults ────────────────────────────────────────────────────────────── local _user_data_dir="${HOME}/.config/chromium" local _backup_dir="" local _profiles=() local _dry_run=0 # ── parse args ──────────────────────────────────────────────────────────── _usage() { cat >&2 <<'EOF' Usage: restore_chrome_bookmarks --backup-dir [--user-data-dir ] [--profile ]... [--dry-run] --user-data-dir Raíz de perfiles destino. Default: ~/.config/chromium --backup-dir Directorio de backup con timestamp generado por backup_chrome_bookmarks. Debe contener subdirectorios /Bookmarks. OBLIGATORIO. --profile Perfil a restaurar (repetible). Si no se pasa ninguno se restauran TODOS los perfiles presentes en backup-dir. --dry-run Muestra qué se copiaría sin tocar nada. Exit codes: 0 éxito (o dry-run completado) 1 error de argumento o validación 2 chromium está corriendo (solo en modo real) EOF return 1 } while [[ $# -gt 0 ]]; do case "$1" in --user-data-dir) _user_data_dir="$2"; shift 2 ;; --backup-dir) _backup_dir="$2"; shift 2 ;; --profile) _profiles+=("$2"); shift 2 ;; --dry-run) _dry_run=1; shift ;; -h|--help) _usage; return 0 ;; *) echo "restore_chrome_bookmarks: argumento desconocido: $1" >&2; return 1 ;; esac done # ── validaciones ────────────────────────────────────────────────────────── if [[ -z "$_backup_dir" ]]; then echo "restore_chrome_bookmarks: --backup-dir es obligatorio" >&2 return 1 fi if [[ ! -d "$_backup_dir" ]]; then echo "restore_chrome_bookmarks: backup-dir no encontrado: ${_backup_dir}" >&2 return 1 fi if [[ ! -d "$_user_data_dir" ]]; then echo "restore_chrome_bookmarks: user-data-dir no encontrado: ${_user_data_dir}" >&2 return 1 fi # ── guard: ningún chromium debe tener ESTE user-data-dir abierto (excepto en dry-run) ── # Por-udd, no global. Comprobamos por PID con comm=chromium (pgrep -x) y leemos su cmdline, # para NO auto-matchear el propio `grep`/`pgrep`: el path del udd contiene "chromium" # (~/.config/chromium-cdp), así que un `pgrep -af '[c]hromium' | grep ` se detecta a sí mismo. if [[ $_dry_run -eq 0 ]]; then local _p _busy=0 for _p in $(pgrep -x chromium 2>/dev/null); do if tr '\0' ' ' < "/proc/$_p/cmdline" 2>/dev/null | grep -qF -- "$_user_data_dir"; then _busy=1; break fi done if [[ $_busy -eq 1 ]]; then echo "restore_chrome_bookmarks: hay un chromium con este user-data-dir abierto — ciérralo antes de restaurar:" >&2 echo " pkill -TERM chromium" >&2 echo "(Chromium reescribe Bookmarks desde memoria al cerrar y desharía la restauración)" >&2 return 2 fi fi # ── determinar perfiles a restaurar ─────────────────────────────────────── local _target_profiles=() if [[ ${#_profiles[@]} -gt 0 ]]; then # Perfiles explícitos: verificar que existen en el backup local _p for _p in "${_profiles[@]}"; do if [[ ! -f "${_backup_dir}/${_p}/Bookmarks" ]]; then echo "restore_chrome_bookmarks: backup no contiene perfil '${_p}': ${_backup_dir}/${_p}/Bookmarks" >&2 return 1 fi _target_profiles+=("$_p") done else # Autodescubrir todos los perfiles en el backup local _profile_path while IFS= read -r -d '' _profile_path; do local _pname _pname="$(basename "$(dirname "$_profile_path")")" _target_profiles+=("$_pname") done < <(find "$_backup_dir" -mindepth 2 -maxdepth 2 -name "Bookmarks" -print0 | sort -z) if [[ ${#_target_profiles[@]} -eq 0 ]]; then echo "restore_chrome_bookmarks: no se encontraron archivos Bookmarks en: ${_backup_dir}" >&2 return 1 fi fi # ── restaurar cada perfil ───────────────────────────────────────────────── local _restored_json="" local _first=1 local _prof for _prof in "${_target_profiles[@]}"; do local _src="${_backup_dir}/${_prof}/Bookmarks" local _dst_dir="${_user_data_dir}/${_prof}" local _dst="${_dst_dir}/Bookmarks" local _dst_bak="${_dst_dir}/Bookmarks.bak" # Tamaño del archivo fuente para el JSON de salida local _bytes=0 if [[ -f "$_src" ]]; then _bytes="$(wc -c < "$_src")" # Eliminar espacios que wc puede añadir en algunas plataformas _bytes="${_bytes// /}" fi if [[ $_dry_run -eq 1 ]]; then echo "=== restore_chrome_bookmarks DRY-RUN ===" >&2 echo " Perfil : ${_prof}" >&2 echo " src : ${_src}" >&2 echo " dst : ${_dst}" >&2 echo " bytes : ${_bytes}" >&2 if [[ -f "$_dst_bak" ]]; then echo " .bak : borraría ${_dst_bak}" >&2 fi else # Crear directorio destino si no existe mkdir -p "$_dst_dir" # Copiar byte a byte preservando timestamps (NUNCA reserializar) cp -p "$_src" "$_dst" # Borrar Bookmarks.bak residual si existe if [[ -f "$_dst_bak" ]]; then rm -f "$_dst_bak" fi fi # Construir fragmento JSON para este perfil local _entry _entry="$(printf '{"profile":"%s","dst":"%s","bytes":%s}' \ "$_prof" "$_dst" "$_bytes")" if [[ $_first -eq 1 ]]; then _restored_json="${_entry}" _first=0 else _restored_json+=",$_entry" fi done # ── emitir resultado JSON ───────────────────────────────────────────────── printf '{"restored":[%s]}\n' "$_restored_json" } # ── auto-ejecución ──────────────────────────────────────────────────────────── if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then restore_chrome_bookmarks "$@" fi