Directorio padre-hijo con el mismo nombre, mover archivos al directorio padre

Directorio padre-hijo con el mismo nombre, mover archivos al directorio padre

Necesito una forma de buscar directorios secundarios con el mismo nombre y luego mover todos los archivos del directorio secundario al principal. Por lo tanto, /recup-dir1/recup-dir1/files to /recup-dir1/files. los directorios secundarios se pueden dejar vacíos porque puedo usar algo como find . -type -d -empty -delete eliminar todos los directorios vacíos.

Entonces el problema es que no tengo idea de en qué directorios están los directorios secundarios con el mismo nombre y en cuáles no.

En pseudocódigo necesito algo como esto.

While more directories are unchecked
get name-x of  next dir
   enter dir  
   If name-x/name-x exist
   move all files in name-x/name-x to name-x
   mark dir as done
next 

Mi mejor suposición es crear un pequeño script en Python para hacer una lista de todos los directorios que tienen un hijo con el mismo nombre y recorrer esta lista a través de un comando como find something something -exec mv

Tal vez esto se pueda hacer con scripts bash o exista otra solución. Como algún comando rsync, sin embargo, dado que creé este desastre probablemente con rsync, no creo que esa sea la solución.

Editar: aquí hay una parte real de la salida del árbol: Los directorios de nivel superior están dentro de /mnt/external-disk/tst-backup No hay subdirectorios en niveles inferiores.

│   └── recup_dir.1
├── recup_dir.10
│   └── recup_dir.10
├── recup_dir.100
│   └── recup_dir.100
├── recup_dir.102
│   └── recup_dir.102
└── recup_dir.1020
    └── recup_dir.1020

Respuesta1

Con zsh, podrías hacer:

for dir in **/*(NDodoN/e['[[ $REPLY:t = $REPLY:h:t ]]']); do
  contents=($dir/*(NDoN))
  (( $#contents == 0 )) ||
    mv -- $contents $dir:h/ &&
    rmdir -- $dir
done

Dónde:

  • **/*(qualifiers)globbing recursivo con calificadores glob
  • N: nullglob: no te quejes si no hay coincidencia
  • D: dotglob: incluye archivos ocultos
  • od: ordene la profundidad primero (las hojas antes de las ramas en las que se encuentran).
  • oN: de lo contrario, no se moleste en ordenar la lista de archivos.
  • /: restringir a archivos de tipo directorio.
  • e['expression']: restringir a archivos para los cuales el expressioncódigo devuelve verdadero (dentro de los cuales se almacena la ruta del archivo actual $REPLY).
  • $REPLY:t: cola (nombre base) del archivo
  • $REPLY:h:t: cola del encabezado (dirname) de los archivos)

Con bash4.4+ y GNU findo la findmayoría de los BSD, podrías hacer algo similar con:

shopt -s nullglob dotglob
readarray -td '' dirs < <(
  LC_ALL=C find . -depth -regex '.*\(/[^/]*\)\1' -type d -print0
)
for d in "${dirs[@]}"; do
  contents=("$d"/*)
  (( ${#contents[@]} == 0 )) ||
    mv -- "${contents[@]}" "${d%/*}/" &&
    rmdir -- "$d"
done

Esta vez usando una expresión regular para hacer coincidir los ./path/to/dir/dirarchivos usando referencias anteriores de expresiones regulares básicas.

Respuesta2

Pruebe esto, basado en GNU findv4.8.0 y Bash v5.1.8

Parte 1: analizar el árbol de directorios + detectar duplicados de nombres de subdirectorios

Supongamos que un determinado directorio en su árbol tiene la siguiente estructura:

./
|__test1/
     |__dirname with space
     |           |__test2
     |                |__ test2
     |__dirname **
     |       |__test1
     |
     |__reboot
     |     |__test1
     | 
     |__test2/
          |__test3/
               |__test2/
                    |__test1/
                         |__test1/

(Hay nombres de directorio extraños para demostrar la seguridad del código).

Verá que algunos subdirectorios (subdirectorios) se repiten de diferentes maneras. Algunos se repiten varias veces, no sólo una vez (por ejemplo test1), uno no se repite ( test3), y pueden repetirse como padre e hijo o separados por un número arbitrario de subdirectorios intermedios.

El siguiente código revela de forma detallada los nombres de subdirectorios duplicados en una estructura de directorios.

  • analiza el árbol de archivos para la estructura del subdirectorio a partir de$PWD
  • encuentra duplicados para cada componente de cualquier ruta de subdirectorio de 2 o más niveles, sin contar el nivel raíz que es $PWD. En mi experimento, la ruta de subdirectorio más larga es: ./test1/test2/test1/test3/test2/test1/test1
  • imprime el primer subdirector duplicado encontrado en cada nivel de subdirectorio, comenzando desde la hoja, es decir, leyendo la ruta del subdirectorio de derecha a izquierda.
  • la impresión se redirige hacia un archivo, en orden inverso, de modo que la ruta del subdirectorio más larga se muestra primero. Dos puntos y coma consecutivos separan los componentes de la ruta (a la izquierda de ";;"), del primer duplicado (a la derecha de ";;") encontrado según la viñeta anterior.

[Código]

$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe"  ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data

$ cat -n  tmp.data

1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1

Parte 2: Procesamiento de nombres de subdirectorios duplicados; contenido en movimiento

El procesamiento se realiza en el orden mostrado en tmp.data.

  • en tmp.datala primera línea, el primer nombre engañado en la ruta ./test1/test2/test1/test3/test2/test1/test1es test1. Podemos transferir su contenido al nivel de subdirectorio más a la izquierda con el mismo nombre:./test1/
  • Una vez que el contenido se ha movido sin afectar los archivos existentes en el destino, test1se elimina el nivel de subdirectorio más a la derecha.
  • Pasamos a la línea 2 de tmp.datay repetimos los pasos anteriores.
  • etc. hasta que tmp.datase hayan consumido todas las líneas.

En esta etapa, la pregunta (para el autor de la pregunta: @TomDerks) es ¿qué hacer con el extremo derecho test1/*de la línea 6? Deberíatodo¿Su contenido se moverá al directorio más a la izquierda con el mismo nombre, que en este caso es el primer nivel de subdirectorio en la ruta? ¿"Todos" incluye archivos en./test1/test2/test1/ así comoel subdirectorio test3y su contenido?
La solución completa (Parte 2) depende de eso.

información relacionada