Estructura de carpeta de aplanamiento

8

Tengo esta estructura de carpetas:

├── foo1
   ├── bar1.txt
   └── bar2.txt
├── foo2
   ├── bar3.txt
   └── bar4 with a space.txt
└── foo3
    └── qux1
        ├── bar5.txt
        └── bar6.txt

que me gustaría aplanar en esto, con un guión bajo entre cada nivel de carpeta:

├── foo1_bar1.txt
├── foo1_bar2.txt
├── foo2_bar3.txt
├── foo2_bar4 with a space.txt
├── foo3_qux1.bar6.txt
└── foo3_qux1_bar5.txt

He buscado y no he encontrado ninguna solución que funcione, principalmente porque mi problema tiene dos particularidades: puede haber más de un nivel de carpeta dentro de la raíz y también porque algunos archivos pueden tener espacios.

¿Alguna idea de cómo lograr esto en bash? ¡Gracias!

Editar : Ejecutando la respuesta propuesta de Gleen Jackman, obtengo esto:

ingrese la descripción de la imagen aquí

Hay dos guiones bajos para la carpeta de primer nivel. ¿Alguna idea de cómo evitar esto o simplemente renombrarlo para que sea solo un guión bajo? Gracias.

nunos
fuente

Respuestas:

15
find */ -type f -exec bash -c 'file=${1#./}; echo mv "$file" "${file//\//_}"' _ '{}' \;

eliminar echosi está satisfecho, está funcionando. No se preocupe si los comandos echo'ed no muestran comillas, el script manejará los archivos con espacios correctamente.

Si desea eliminar los subdirectorios ahora vacíos:

find */ -depth -type d -exec echo rmdir '{}' \; 
Glenn Jackman
fuente
Oye. ¡Eso es genial! Gracias. Eso hizo el truco casi por completo. Solo tengo un problema menor, que es el que obtengo foo1__qux1_bar5.txt, dos guiones bajos en el primer nivel de carpeta que es. Intenté jugar con tu línea de bash pero nada parecía funcionar. ¿Quizás necesito agregar algo?
nunos
@nunos, no puedo reproducir ese comportamiento
glenn jackman
De Verdad? Extraño. Acabo de actualizar mi respuesta con más información. ¿Puedes por favor echar un vistazo? Gracias.
nunos
1
eh OK, cambie "${file//\//_}"a "${file//+(/)/_, y si esto es un guión,shopt -s extglob
Glenn Jackman
También tuve el problema con los dos guiones bajos. Desafortunadamente, cambiar el comando como sugirió no funcionó para mí. Sin embargo, encontré esta respuesta en AskDifferent: apple.stackexchange.com/a/254224/122152 Según la respuesta, macOS podría estar usando BSD find. Una solución alternativa puede ser usar findutils. Se puede instalar usando homebrew con brew install findutilsy luego usando en gfindlugar de find. Esto funcionó para mí.
intagli
5

Usando el cambio de nombre de perl:

find . -depth -type f -exec rename 's@(?<!\.)/@_@g' -- {} \;

SALIDA

$ find -type f
./foo2_bar4 whit a space.txt
./foo3_qux1_bar6.txt
./foo2_bar3.txt
./foo3_qux1_bar5.txt
./foo1_bar2.txt
./foo1_bar1.txt

NOTA

  • Yo uso una mirada negativa hacia atrás (?<!\.) para no tocar el primero./
  • Mantengo los directorios vacíos, no dudes en:

    find . -depth -type d -exec rm {} \;

advertencia Hay otras herramientas con el mismo nombre que pueden o no ser capaces de hacer esto, así que tenga cuidado.

Si ejecuta el siguiente comando ( GNU)

$ file "$(readlink -f "$(type -p rename)")"

y tienes un resultado como

.../rename: Perl script, ASCII text executable

y no contiene:

ELF

entonces esta parece ser la herramienta correcta =)

Si no, para que sea el valor predeterminado (generalmente ya es el caso) Debiany derivado como Ubuntu:

$ sudo update-alternatives --set rename /path/to/rename

(reemplazar /path/to/renamea la ruta de su perl's renamecomando.


Si no tiene este comando, busque su administrador de paquetes para instalarlo o hágalo manualmente


Por último, pero no menos importante, esta herramienta fue originalmente escrita por Larry Wall, el padre de Perl.

Gilles Quenot
fuente
... -type d -exec rmdir ...?
JJoao
Parece que no tengo disponible el cambio de nombre. ¿Cómo puedo obtenerlo? ¿Está normalmente disponible en un sistema UNIX? Gracias.
nunos
@nunos Es un guión de Perl. Está disponible bajo el nombre perl-renameen Arch Linux y prenameen sistemas basados ​​en Debian.
Muru
renameExplicaciones agregadas
Gilles Quenot
1

con pax...

pax -Xrwls '|/|_|g' */ "$PWD"

Esto creará un enlace duro en el directorio actual a todos los archivos en sus directorios secundarios con una _sustituido por /. Luego puede inspeccionar los resultados y eliminar todos los directorios secundarios con ...

rm -rf */

... una vez que haya verificado los resultados son de su agrado.

mikeserv
fuente
0

Puedes probar con el siguiente comando.

$ find -type f -exec bash -c 'mv $0 $(echo $0|sed "s/\//_/2")' {} \;

El comando anterior moverá los archivos y permanecerá el directorio que puede eliminarse más tarde.

$ ls
foo1_bar1.txt  foo1_bar2.txt  foo2_bar3.txt  foo2_bar4.txt  foo3_bar5.txt  foo3_bar6.txt
Kannan Mohan
fuente