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 globN
: nullglob: no te quejes si no hay coincidenciaD
: dotglob: incluye archivos ocultosod
: 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 elexpression
có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 bash
4.4+ y GNU find
o la find
mayorí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/dir
archivos usando referencias anteriores de expresiones regulares básicas.
Respuesta2
Pruebe esto, basado en GNU find
v4.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.data
la primera línea, el primer nombre engañado en la ruta./test1/test2/test1/test3/test2/test1/test1
estest1
. 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,
test1
se elimina el nivel de subdirectorio más a la derecha. - Pasamos a la línea 2 de
tmp.data
y repetimos los pasos anteriores. - etc. hasta que
tmp.data
se 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 test3
y su contenido?
La solución completa (Parte 2) depende de eso.