Cambiar el nombre de archivos en diferentes subcarpetas

Cambiar el nombre de archivos en diferentes subcarpetas

Me gustaría cambiar el nombre de varios archivos que se encuentran en diferentes carpetas. Más específicamente, me gustaría agregar ceros en el medio del nombre del archivo, para que todos los nombres de archivo tengan tres dígitos (y por lo tanto aparezcan en un orden lógico).

Para ser más específico, tengo más de 130 carpetas con más de 400 archivos .nii en cada carpeta. Cada archivo .nii tiene el siguiente patrón:

swu_run1_P_139_Vol_1.nii

  • P_### varía de P76 a P277 (número de participante)
  • Vol_### varía de 1 a 405 (número de volumen)

Como el volumen varía de 1 a 405, significa que cualquier lista comienza con 100, en lugar de ir del 1 al 405 (por ejemplo, comienza: 100 - 101 - 102 [..] - 109 -10- 110 - 111 etc.). Una forma de resolver esto es agregar ceros al nombre del archivo y hacer que todo tenga tres dígitos, por ejemplo:

swu_run1_P_277_Vol_1.nii -> swu_run1_P_277_Vol_001.nii
swu_run1_P_277_Vol_2.nii -> swu_run1_P_277_Vol_002.nii
swu_run1_P_277_Vol_10.nii -> swu_run1_P_277_Vol_010.nii
swu_run1_P_277_Vol_120.nii -> swu_run1_P_277_Vol_120.nii

Tengo poca experiencia con sistemas Unix/Linux, pero usando hilos anteriores, logré generar el siguiente código. Tiene dos partes:

1. Cambie el nombre de varios archivos, agregando ceros en el medio del nombre del archivo:

rename Vol_ Vol_0 *Vol_[0-9].nii

Si ejecuto esto en una subcarpeta, aparece el siguiente mensaje de error:

Error: rename: swu_run1_P_275_Vol_1.nii: rename to swu_run1_P_275_Vol_01.nii failed: No such file or directory

Por extraño que parezca, está agregando el cero al Vol_1-9. Sin embargo, no añade un cero a ningún número que ya tenga dos o tres dígitos:

swu_run1_P_277_Vol_1.txt -> swu_run1_P_277_Vol_01.nii
swu_run1_P_277_Vol_10.txt -> swu_run1_P_277_Vol_10.nii
swu_run1_P_277_Vol_100.txt -> swu_run1_P_277_Vol_100.nii

¿Parece que hay algún tipo de bucle extraño con la expresión (intenta cambiar el nuevo Vol_01 y muestra el mensaje de error)? ¿Y por qué no agrega un cero a los dos/tres dígitos?

2. Busque todos .niilos archivos en las subcarpetas relevantes y luego cambie el nombre por lotes:

find . -iname "*.nii" -execdir rename Vol_ Vol_0 *Vol_[0-9].nii '{}' \;

Mi comprensión de este código es la siguiente:

  • find . -iname "*.nii"busca todos los archivos .nii, tanto en la carpeta actual como en las subcarpetas

  • -execdirle dice que aplique la siguiente expresión a la carpeta actual y las subcarpetas, es decir, agregando un cero

  • rename Vol_ Vol_0 *Vol_[0-9].niiagrega ese cero (usando el formato dedel texto textear lista de archivos)

  • '{}'está ahí para el nombre de la ruta del archivo

  • \;¿Está ahí para finalizar la expresión -execdir?

Si intento ejecutar el código en más carpetas, aparece el siguiente mensaje de error:

Error: rename: *Vol_[0-9].nii: rename to *Vol_0[0-9].nii failed: No such file or directory

Creo que recibo el mensaje de error porque -execdir no se ejecuta en las subcarpetas, pero no sé cómo solucionarlo.

Preferiría no ingresar manualmente a cada subcarpeta para ejecutar el shell, entonces, ¿tiene alguna sugerencia sobre cómo mejorar este código (y hacerlo funcionar)? ¿Y por qué recibo el mensaje de error "No existe tal archivo o directorio"?

Respuesta1

Si tuviéramos el nombre swu_run1_P_277_Vol_1.niien la variable name, entonces ${name##*_}sería ( se elimina 1.niila coincidencia de cadena de prefijo más larga ).*_

Tomar eso y eliminarlo .niinos deja con el número que queremos llenar con ceros hasta un ancho de tres caracteres:

name=swu_run1_P_277_Vol_1.nii

number=${name##*_}
number=${number%.nii}

En el bashshell, la forma más fácil (posiblemente) de completar el número con ceros es con printf:

printf '%.3d' "$number"    # would print 001 (with no newline)

Podemos construir el nuevo nombre al mismo tiempo:

printf '%s_%.3d.nii' "${name%_*.nii}" "$number"

Ese printfcomando se imprimiría swu_run1_P_277_Vol_001.niieliminando la cadena de sufijo que coincide _*.niicon el nombre original, agregando _el número con ceros y luego la .niicadena de sufijo.

Para colmo, podemos imprimir la cadena resultante.directamenteen una nueva variable con printf -v newname ....

Juntando esto para un solo nombre:

name=swu_run1_P_277_Vol_1.nii

number=${name##*_}
number=${number%.nii}

printf -v newname '%s_%.3d.nii' "${name%_*.nii}" "$number"

Entonces es sólo una cuestión de mv "$name" "$newname".

Bien, entonces, ¿cómo hacerlo para todos los archivos relevantes?

Supongamos que todos los archivos relevantes coinciden con el patrón global *Vol_*.nii, entonces, con find,

find . -type f -name '*Vol_*.nii' -exec bash -c '
    for pathname do
        dirpath=${pathname%/*}  # or: dirpath=$(dirname "$pathname")
        name=${pathname##*/}    # or: name=$(basename "$pathname")

        number=${name##*_}
        number=${number%.nii}

        printf -v newname "%s_%.3d.nii" "${name%_*.nii}" "$number"

        printf "Would rename %s --> %s\n" "$pathname" "$dirpath/$newname"
        # mv "$pathname" "$dirpath/$newname"
    done' bash {} +

El bash -cscript en línea aquí es llamado findcon lotes de nombres de rutas encontrados que cumplen con los criterios -type f(es un archivo normal) y -name(tiene un nombre particular). El script recorre estos nombres de ruta y extrae el nombre del archivo namey el nombre de la ruta del directorio dirpath.

Luego realiza las mismas operaciones que hicimos antes para llegar a un nuevo nombre, que se almacena en newnamey luego cambia el nombre del archivo.

Bueno, he comentado el mvcomando real por seguridad. Primero debe ejecutar esto una vez y ver que el resultado sea correcto. Si utiliza herramientas GNU, es posible que también desee utilizarlas mv -bpara realizar copias de seguridad de archivos si hay colisiones de nombres.


Como nota al margen, en el zshcaparazón, el patrón globular

./**/*Vol_*.nii(n)

se expandiría a todos esos nombresen orden numérico(y recursivamente hacia abajo en subdirectorios):

$ print -rC1 ./**/*Vol_*.nii(n)
./swu_run1_P_277_Vol_1.nii
./swu_run1_P_277_Vol_2.nii
./swu_run1_P_277_Vol_10.nii
./swu_run1_P_277_Vol_120.nii

información relacionada