domingo, 12 de octubre de 2025

COPIAR CARPETAS pero evitando duplicados internos y excluyendo algunas carpetas y extensiones de ficheros

 Introducción

Lo vamos a hacer en 2 pasos:

  1. Detectaremos duplicados por nombre y tamaño (excluyendo algunas carpetas y extensiones de ficheros) creando un fichero con la lista de duplicados
  2. Copiaremos los ficheros excluyendo los ficheros de la lista guadada en el fichero generado anteriormente y las carpetas y ficheros excluidos por extensión

1. Generar una lista de ficheros duplicados:


  • Las líneas 35 a 39 indican las carpetas a excluir
  • Las lÍneas 42 a 44 indica las extensiones de ficheros a excluir
  • Al final nos da el nombre del fichero generado
  • Para ejecutar el script hay que pasarle la carpeta a analizar
Ejecutamos este  script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/bin/bash
# buscar_duplicados_exacto_final.sh
# Autor: Ximo Dante con IA
# Busca archivos con el mismo nombre y tamaño exacto,
# excluyendo carpetas del sistema y archivos temporales.
# Marca con "#" las cabeceras y el fichero más superficial.

set -euo pipefail
IFS=$'\n'

BASE_DIR="${1:-}"

if [[ -z "$BASE_DIR" ]]; then
  echo "❌ Uso: $0 /ruta/a/carpeta"
  exit 1
fi
if [[ ! -d "$BASE_DIR" ]]; then
  echo "❌ No existe el directorio: $BASE_DIR"
  exit 1
fi

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUT="duplicados_final_${TIMESTAMP}.txt"

echo "🔍 Buscando duplicados exactos en: $BASE_DIR"
echo "📝 Informe: $OUT"
echo

# ------------------------------------------------------------
# 1️⃣ Buscar y procesar con find + awk (sin bucles)
# ------------------------------------------------------------
find "$BASE_DIR" \
  \( -type d \( \
      -name ".*" -o -name "\$*" \
      -o -iname "Archivos de programa" -o -iname "Descargas" -o -iname "Downloads" \
      -o -iname "Documents and Settings" -o -iname "Logs" -o -iname "PerfLogs" \
      -o -iname "Program Files" -o -iname "Program Files (x86)" -o -iname "ProgramData" \
      -o -iname "Recovery" -o -iname "System Volume Information" -o -iname "Temp" \
      -o -iname "Users" -o -iname "Windows" -o -iname "Windows10Upgrade" \
    \) -prune \) \
  -o -type f \
     ! -iname "*.exe" ! -iname "*.log" ! -iname "*.dll" ! -iname "*.sys"\
     ! -iname "*.part" ! -iname "*.crdownload" ! -iname "*.tmp" \
     ! -iname "*.partial" ! -iname "*.download" ! -iname "*!qB*" ! -iname "*.aria2" \
  -printf '%f\t%s\t%p\n' 2>/dev/null \
| sort -k1,1 -k2,2n \
| awk -F'\t' -v base="$BASE_DIR" '
  function depth(path,   nf, nb, a, b) {
    nf = split(path, a, "/");
    nb = split(base, b, "/");
    return nf - nb - 1;
  }

  {
    name=$1; size=$2; path=$3;
    # Ignorar temporales (~$, _~, ~.)
    if (name ~ /^~[$]|^_~|^~\./) next;
    key = name "|" size;
    files[key] = files[key] ? files[key] RS path : path;
  }
  END {
    dup_groups=0;
    for (k in files) {
      n = split(files[k], arr, "\n");
      if (n > 1) {
        dup_groups++;
        split(k, parts, "|");
        print "#-----------------------------------";
        print "#📂 Duplicado exacto: " parts[1] " (" parts[2] " bytes)";
        # Fichero más superficial (menor profundidad)
        min_d=1e9; idx=0;
        for (i=1; i<=n; i++) {
          d = depth(arr[i]);
          depths[i]=d;
          if (d < min_d) { min_d=d; idx=i; }
        }
        for (i=1; i<=n; i++) {
          mark = (i==idx) ? "#" : " ";
          printf "%s%s [nivel=%d]\n", mark, arr[i], depths[i];
        }
      }
    }
    if (dup_groups==0)
      print "ℹ️ No se encontraron duplicados exactos (nombre + tamaño)" > "/dev/stderr";
    else
      print "✅ Se encontraron " dup_groups " grupos de duplicados." > "/dev/stderr";
  }
' > "$OUT"

echo
if [[ -s "$OUT" ]]; then
  echo "✅ Informe generado: $(realpath "$OUT")"
else
  echo "ℹ️ El fichero está vacío: no se detectaron duplicados exactos tras exclusiones."
fi
echo


2. Copia de carpetas en base al fichero de duplicados:

  • Las líneas 55 a 59 indican las carpetas a excluir
  • Las lÍneas 62 a 64 indica las extensiones de ficheros a excluir
  • Al final nos da el nombre del fichero generado con la lista de ficheros copiados
  • Para ejecutar el script hay que pasarle las carpeta de origen y destino y la ruta del fichero de duplicados generados en el punto anterior
Veamos el script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/bin/bash
# ============================================================
# 🧰 copiar_no_duplicados.sh
# Autor: Ximo Dante + IA
# Copia solo los ficheros no duplicados ni excluidos
# según el informe generado por buscar_duplicados_exacto_final.sh
# Sin usar rsync — usa find + cp --parents (más estable)
# ============================================================

set -euo pipefail
IFS=$'\n'

ORIGEN="${1:-}"
DESTINO="${2:-}"
DUP_FILE="${3:-}"

if [[ -z "$ORIGEN" || -z "$DESTINO" || -z "$DUP_FILE" ]]; then
  echo "❌ Uso: $0 /carpeta/origen /carpeta/destino duplicados_final_xxx.txt"
  exit 1
fi

if [[ ! -d "$ORIGEN" ]]; then
  echo "❌ Carpeta origen no existe: $ORIGEN"
  exit 1
fi
if [[ ! -f "$DUP_FILE" ]]; then
  echo "❌ No existe el fichero de duplicados: $DUP_FILE"
  exit 1
fi

mkdir -p "$DESTINO"
TS=$(date +%Y%m%d_%H%M%S)
LOG="$DESTINO/copiar_no_duplicados_${TS}.log"

echo "📂 Carpeta origen: $ORIGEN"
echo "📁 Carpeta destino: $DESTINO"
echo "🧾 Archivo de duplicados: $DUP_FILE"
echo "📝 Log: $LOG"
echo

# ------------------------------------------------------------
# 1️⃣ Construir lista de rutas a excluir
# ------------------------------------------------------------
EXCL_TMP=$(mktemp)
grep -v '^#' "$DUP_FILE" | grep -E '^/' > "$EXCL_TMP" || true

# ------------------------------------------------------------
# 2️⃣ Encontrar archivos válidos (no duplicados ni excluidos)
# ------------------------------------------------------------
echo "🔎 Escaneando archivos válidos..."

find "$ORIGEN" \
  \( -type d \( \
      -name ".*" -o -name "\$*" \
      -o -iname "Archivos de programa" -o -iname "Descargas" -o -iname "Downloads" \
      -o -iname "Documents and Settings" -o -iname "Logs" -o -iname "PerfLogs" \
      -o -iname "Program Files" -o -iname "Program Files (x86)" -o -iname "ProgramData" \
      -o -iname "Recovery" -o -iname "System Volume Information" -o -iname "Temp" \
      -o -iname "Users" -o -iname "Windows" -o -iname "Windows10Upgrade" \
    \) -prune \) \
  -o -type f \
     ! -iname "*.exe" ! -iname "*.log" ! -iname "*.dll" ! -iname "*.sys" \
     ! -iname "*.part" ! -iname "*.crdownload" ! -iname "*.tmp" \
     ! -iname "*.partial" ! -iname "*.download" ! -iname "*!qB*" ! -iname "*.aria2" \
  -print0 2>/dev/null \
| grep -zavFf "$EXCL_TMP" \
| tee >(xargs -0 -I{} bash -c '
      FILE="{}"
      REL="${FILE#'"$ORIGEN"'/}"
      DEST_FILE="'"$DESTINO"'/$REL"
      mkdir -p "$(dirname "$DEST_FILE")"
      cp -p "$FILE" "$DEST_FILE"
      echo "✔ Copiado: $REL" >> "'"$LOG"'"
  ') > /dev/null

# ------------------------------------------------------------
# 3️⃣ Mostrar resumen
# ------------------------------------------------------------
COPIADOS=$(grep -c "✔ Copiado:" "$LOG" || echo 0)
echo
echo "✅ Copia completada."
echo "📦 Archivos copiados: $COPIADOS"
echo "📄 Log detallado: $LOG"
echo




No hay comentarios :

Publicar un comentario