3 votos

¿Alguien puede mejorar mi script de cambio de nombre por lotes para que sea recursivo?

Tengo un NAS Synology que se volvió loco y está lleno de archivos .tmp que solían ser de Excel, PDF, Word, etc.,...

Tengo este script que funciona bien dentro de una carpeta. He buscado durante días antes de preguntar aquí.

¿Cómo puedo hacer que sea recursivo para todas las subcarpetas?

for f in *.tmp; do
    ext=''
    case $(file -b "$f") in
        *Excel*) ext='.xlsx' ;;
        *Word*)  ext='.docx' ;;
        *PowerPoint*)  ext='.pptx' ;;
        *PDF*)   ext='.pdf' ;;
        # etc...
        *) continue ;;
    esac
    mv "${f}" "${f}${ext}"
done

3voto

Suponiendo que desee aplicar las substituciones a todos los subdirectorios

find . -type f -name '*.tmp' -exec sh -c '
    f=$1
    case $(file -b "$f") in
        *Excel*) ext=.xlsx ;;
        *Word*)  ext=.docx ;;
        *PowerPoint*)  ext=.pptx ;;
        *PDF*)   ext=.pdf ;;
        # etc...
        *) continue ;;
    esac
    echo mv "${f}" "${f}${ext}"' _ {} \;

Elimine el echo una vez que haya verificado que la salida tenga sentido.

2voto

Greenonline Puntos 373

No creo que te refieras a recursivo, ya que eso implicaría que el script se llama a sí mismo. Probablemente quieres buscar subdirectorios de forma iterativa.

Arreglos simples a tu script original

Intenta agregar */ a *.tmp, de la siguiente manera:

for f in */*.tmp; do
    ext=''
    case $(file -b "$f") in
        *Excel*) ext='.xlsx' ;;
        *Word*)  ext='.docx' ;;
        *PowerPoint*)  ext='.pptx' ;;
        *PDF*)   ext='.pdf' ;;
        # etc...
        *) continue ;;
    esac
    mv "${f}" "${f}${ext}"
done

Lo anterior funcionará solo para un nivel de subdirectorios (no mencionas en tu respuesta qué tan profundos son tus subdirectorios).

Si tienes múltiples niveles de subdirectorios, entonces deberías usar el siguiente script en su lugar (que utiliza **/*.tmp en lugar de */*.tmp):

for f in **/*.tmp; do
    ext=''
    case $(file -b "$f") in
        *Excel*) ext='.xlsx' ;;
        *Word*)  ext='.docx' ;;
        *PowerPoint*)  ext='.pptx' ;;
        *PDF*)   ext='.pdf' ;;
        # etc...
        *) continue ;;
    esac
    mv "${f}" "${f}${ext}"
done

Esto funcionará bien en ksh.

Sin embargo, si estás usando bash (versión 4 y superior), entonces necesitarás agregar al principio del script shopt -s globstar, de la siguiente manera:

shopt -s globstar

for f in **/*.tmp; do
    ext=''
    case $(file -b "$f") in
        *Excel*) ext='.xlsx' ;;
        *Word*)  ext='.docx' ;;
        *PowerPoint*)  ext='.pptx' ;;
        *PDF*)   ext='.pdf' ;;
        # etc...
        *) continue ;;
    esac
    mv "${f}" "${f}${ext}"
done

Mira esta respuesta a ¿Globo recursivo? para conocer las diversas advertencias al usar globos en bash.


Recursión real

Si realmente quieres una forma recursiva de hacer las cosas, podrías usar el siguiente script.

Pasa a través del contenido del directorio, como antes (en tu versión original), pero si encuentra un subdirectorio, se llama a sí mismo (es decir, recursividad) y comienza un nuevo script (algo así - ve la última línea), y así sucesivamente. Una vez que se hayan agotado el subdirectorio (o subdirectorios), el script llamado recursivamente finaliza y el script que llama continúa su camino feliz - y así sucesivamente.

Recibe un parámetro, la ruta a tu directorio que deseas buscar, por lo que ahora no tienes que ejecutar el script mientras estás en el directorio a buscar (puedes ejecutarlo desde cualquier lugar):

#!/bin/bash

search_dirs () {
    shopt -s nullglob dotglob

    for the_path in "$1"/*; do
        if [ -d "$the_path" ]; then
            search_dirs "$the_path"
        else # Tu script original
            ext=''
            case $(file -b "$the_path") in
                *Excel*) ext='.xlsx' ;;
                *Word*)  ext='.docx' ;;
                *PowerPoint*)  ext='.pptx' ;;
                *PDF*)   ext='.pdf' ;;
                # etc...
                *) continue ;;
            esac
            mv "${the_path}" "${the_path}${ext}"
            printf '%s renombrado a %s.\n' "${the_path}" "${the_path}${ext}"
        fi
    done
}

Para ser más precisos, no es el script lo que es recursivo, es la función, search_dirs(), la que es recursiva.

Referencia:

Usando find ... -exec

nohillside me ha ganado con una versión superior, pero aquí hay un par de comandos de una sola línea (desordenados).

Alternativamente, podrías usar find -exec, que es un poco más exhaustivo al recorrer subdirectorios. Entonces, algo como:

find . -type f -exec bash -c '[[ $(file -b "'{}'") == *"PDF"* ]] ' \; -exec sh -c '
for src; do
  mv "$src" "${src}.pdf"
done' sh {} +

para cada una de las breves descripciones de archivo (file -b), es decir, Excel, Word, PowerPoint, PDF, y así sucesivamente.

O (esencialmente lo mismo):

find . -type f -exec bash -c '[[ $(file -b "'{}'") == *"PDF"* ]] ' \; -exec bash -c 'mv $0 ${0.pdf}' "{}" \;

Referencias:

2voto

staffan Puntos 3299

Utilice zsh para cualquier manipulación de archivos que no necesite ser portable. Es simplemente más simple (incluso si los beneficios son relativamente limitados en su caso en comparación con las típicas preguntas de renombrar archivos).

Para recorrer subdirectorios de forma recursiva, use **/.

for f in **/.tmp; do
  …

Para renombrar por lotes, utilice la función zmv. Se encarga de muchas cosas, incluida la verificación de los objetivos existentes, la verificación de duplicados (dos archivos que se renombran con el mismo nombre), manipulación correcta de todos los nombres de archivos (su script falla si un nombre de archivo comienza con un -), etc. Omite archivos si su nuevo nombre es idéntico a su antiguo nombre.

Si no está seguro, ejecute zmv -n … primero para ver una impresión de lo que haría. Si está satisfecho, ejecute el comando nuevamente sin la opción -n.

Aquí hay una versión de zmv de su tarea.

#!/usr/bin/env zsh
autoload zmv
function guess_extension {
  local ext=
  case $(file -b -- $1) in
    *Excel*) ext=.xlsx;;
    …
  esac
  print -r $1$ext
}
zmv '**/*.tmp' '$(guess_extension $f)'

Aquí hay una versión modificada que elimina la parte .tmp.

#!/usr/bin/env zsh
autoload zmv
function guess_extension {
  local ext=
  case $(file -b -- $1) in
    *Excel*) ext=.xlsx;;
    …
  esac
  if [[ -n $ext ]]; then print -r ${1%.tmp}$ext; else print -r $1; fi
}
zmv '**/*.tmp' '$(guess_extension $f)'

Recomiendo usar file -I o file --mime-type para una mayor solidez. Por ejemplo, case $(file -b <$1) in *Excel*) … atrapa plantillas de Excel que deberían tener la extensión .xltx. Puede hacer que esa coincidencia sea más precisa con case $(file --mime-type -b -- $1) in application/vnd.ms-excel) ….

AppleAyuda.com

AppleAyuda es una comunidad de usuarios de los productos de Apple en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X