Agrupar archivos en algunas carpetas

12

Tengo algunos archivos con diferentes extensiones como *.pdf, *.mp3, *.jpgy algunos otros. Todos ellos se almacenan en un parentdirectorio.

¿Cómo puedo obtener una lista de todas las extensiones, crear algunas carpetas basadas en estas extensiones y luego mover todos los archivos a sus carpetas relevantes?

αғsнιη
fuente

Respuestas:

13

El script de Python a continuación hace el trabajo. Los archivos ocultos se almacenan por separado en una carpeta, así como los archivos sin extensión.

Como podría usarse para una gama más amplia de propósitos, agregué algunas opciones:

  • Puede establecer extensiones que le gustaría excluir de la "reorganización". Si simplemente quiere mover todo, configureexclude = ()
  • Puedes elegir qué hacer con las carpetas vacías ( remove_emptyfolders = Trueo False)
  • En caso de que desee copiar los archivos en lugar de moverlos , reemplace la línea:
shutil.move(subject, new_dir+"/"+name)

por:

shutil.copy(subject, new_dir+"/"+name) 

La secuencia de comandos:

#!/usr/bin/env python3

import os
import subprocess
import shutil

# --------------------------------------------------------
reorg_dir = "/path/to/directory_to_reorganize"
exclude = (".jpg") # for example
remove_emptyfolders = True
# ---------------------------------------------------------

for root, dirs, files in os.walk(reorg_dir):
    for name in files:
        subject = root+"/"+name
        if name.startswith("."):
            extension = ".hidden_files"
        elif not "." in name:
            extension = ".without_extension"
        else:
            extension = name[name.rfind("."):]
        if not extension in exclude:
            new_dir = reorg_dir+"/"+extension[1:]
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            shutil.move(subject, new_dir+"/"+name)

def cleanup():
    filelist = []
    for root, dirs, files in os.walk(reorg_dir):
        for name in files:
            filelist.append(root+"/"+name)
    directories = [item[0] for item in os.walk(reorg_dir)]
    for dr in directories:
        matches = [item for item in filelist if dr in item]
        if len(matches) == 0:
            try:
                shutil.rmtree(dr)
            except FileNotFoundError:
                pass

if remove_emptyfolders == True:
    cleanup()

SI existe el riesgo de sobrescribir archivos duplicados no deseados

A expensas de algunas líneas adicionales, podemos evitar sobrescribir posibles duplicados. Con el siguiente código, los duplicados se renombrarán como:

duplicate_1_filename, duplicate_2_filename 

etc.

La secuencia de comandos:

#!/usr/bin/env python3

import os
import subprocess
import shutil

# --------------------------------------------------------
reorg_dir = "/path/to/directory_to_reorganize"
exclude = (".jpg") # for example
remove_emptyfolders = True
# ---------------------------------------------------------

for root, dirs, files in os.walk(reorg_dir):
    for name in files:
        subject = root+"/"+name
        if name.startswith("."):
            extension = ".hidden_files"
        elif not "." in name:
            extension = ".without_extension"
        else:
            extension = name[name.rfind("."):]
        if not extension in exclude:
            new_dir = reorg_dir+"/"+extension[1:]
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            n = 1; name_orig = name
            while os.path.exists(new_dir+"/"+name):
                name = "duplicate_"+str(n)+"_"+name_orig
                n = n+1
            newfile = new_dir+"/"+name
            shutil.move(subject, newfile)

def cleanup():
    filelist = []
    for root, dirs, files in os.walk(reorg_dir):
        for name in files:
            filelist.append(root+"/"+name)
    directories = [item[0] for item in os.walk(reorg_dir)]
    for dr in directories:
        matches = [item for item in filelist if dr in item]
        if len(matches) == 0:
            try:
                shutil.rmtree(dr)
            except FileNotFoundError:
                pass

if remove_emptyfolders == True:
    cleanup()

EDITAR

Con OP en mente, todos olvidamos agregar una instrucción sobre cómo usar. Dado que pueden aparecer ( y aparecen) preguntas duplicadas , sin embargo, puede ser útil.

Cómo utilizar

  1. Copie cualquiera de los scripts en un archivo vacío, guárdelo como reorganize.py
  2. En la sección principal del script, configure el directorio de destino (con los archivos para reorganizar):

    reorg_dir = "/path/to/directory_to_reorganize" 

    (use comillas si el directorio contiene espacios)

    posibles extensiones que desea excluir (probablemente ninguna, como a continuación):

    exclude = ()

    y si luego desea eliminar carpetas vacías:

    remove_emptyfolders = True
  3. Ejecute el script con el comando:

    python3 /path/to/reorganize.py

Nota: si desea copiar los archivos en lugar de moverlos , reemplace:

shutil.move(subject, new_dir+"/"+name)

por:

shutil.copy(subject, new_dir+"/"+name)

Por favor, intente primero con una muestra pequeña.

Jacob Vlijm
fuente
12

Puede usar findcon un execcomando algo complejo :

find . -iname '*?.?*' -type f -exec bash -c 'EXT="${0##*.}"; mkdir -p "$PWD/${EXT}_dir"; cp --target-directory="$PWD/${EXT}_dir" "$0"' {} \;

# '*?.?*' requires at least one character before and after the '.', 
# so that files like .bashrc and blah. are avoided.
# EXT="${0##*.}" - get the extension
# mkdir -p $PWD/${EXT}_dir - make the folder, ignore if it exists

Reemplace cpcon echopara un funcionamiento en seco.


Más eficiente y ordenado sería guardar el bashcomando en un script (por ejemplo, en /path/to/the/script.sh):

#! /bin/bash

for i
do
    EXT="${i##*.}" 
    mkdir -p "$PWD/${EXT}_dir"
    mv --target-directory="$PWD/${EXT}_dir" "$i" 
done

Y luego corre find:

find . -iname '*?.?*' -type f -exec /path/to/the/script.sh {} +

Este enfoque es bastante flexible. Por ejemplo, para usar el nombre de archivo en lugar de la extensión ( filename.ext), usaríamos esto para EXT:

NAME="${i##*/}"
EXT="${NAME%.*}"
muru
fuente
+1; El -iname '*.*'debería ocuparse de los casos de esquina que me preocupaban ... ¡Buena idea!
Rmano
@Rmano no es el *.fig.bako .profile/.bashrclos, pero al menos solo debe manejar archivos con extensiones. Gracias.
muru
6
ls | gawk -F. 'NF>1 {f= $NF "-DIR"; system("mkdir -p " f ";mv " $0 " " f)}'

Cálculo de la lista de extensiones (después de mover):

ls -d *-DIR

Cálculo de la lista de extensiones (antes de mover):

ls -X | grep -Po '(?<=\.)(\w+)$'| uniq -c | sort -n

(en este último ejemplo, estamos calculando el número de archivos para cada extensión y ordenándolos)


fuente
1
lástima: un error tipográfico "mkdir -f" se corrigió a "mkdir -p" (ignorar si dir existe)
¿No se supone que uniq se aplica después de la ordenación? Y no analice la salida de ls.
muru
@muru, (parte 1) ls -X garantiza que las extensiones estén ordenadas. La clasificación final fue solo para ordenar la tabla de extensiones por número de ocurrencias - relevancia. (¿Estoy en lo correcto?).
@muru, (parte 2) ls -X | grep -Po '(?<=\.)(\w+)$'fue mi primera idea para obtener la lista ordenada de extensiones. ¿Es muy malo? ¿Que sugieres?
Olvidé lo que ls -Xhace. En cuanto a por qué lo recomiendo ls, consulte unix.stackexchange.com/q/128985/70524 y unix.stackexchange.com/q/112125/70524 . Para lograr lo que haces, iría por un camino más largo: find . -type f -name '*?.?*' -print0 | sed -z 's/.*\.//' | sort -zu(con un opcional | uniq -cz, si se necesitan conteos). Y find ... -print0 | gawk -v RS='\0'(aunque eso no es muy portátil ) para el primero.
muru
5

Prueba este script de shell.

#!/bin/sh
src=`dirname "$1"`/`basename "$1"`;
for file in "$src"/*?.?*; do
  if test -f "$file"; then
    dest="$src${file##*.}"_files;
    mkdir -p "$dest";
    mv "$file" "$dest";
  fi;
done;

# pass the directory to re-organize as first argument
# moves only regular files which have extension
# ignores other type of files including
# files having no extension, hidden files, directories, and links.
Prashant Karmakar
fuente
1
Lo siento, eso es un error. Debería tener que sustituir cada aparición de filepathcon file. Lo corregiré directamente.
Prashant Karmakar
No analice la salida de ls. En cambio, hazlofor file in "$src"/*?.?*; do ..
muru
@muru, ¿funcionará correctamente si el nombre de algún archivo tiene espacios?
Prashant Karmakar
@PrashantKarmakar sí, mientras que readpuede tener un comportamiento inesperado. También debe citar las variables en los comandos mkdir y mv.
muru
Pruébalo, si quieres:for i in *; do printf "%s\n" "$i"; done; for i in $(ls -d); do printf "%s\n" "$i"; done
muru
2

Si tiene instalado el cambio de nombre / nombre de Perl:

rename 's!(.*)\.(\w+)$! mkdir($2); "$2/$&"!ge'  *
muru
fuente