Executando verificação de segurança...
3

Como fiz um organizador automático de arquivos

Recentemente abrindo a minha pasta de downloads percebi o quão desorganizada ela é e o quão aleatórios são os arquivos que estão nela. Isso é um problema recorrente com 99,9999% dos usuários médios de computadores. É aquele velho assunto de "depois eu organizo", mas esse "depois" nunca chega. Eu sou usuário de Linux, mais precisamente o Arch Linux e o que mais me chama a atenção em relação à organização da minha home é o fato de a pasta de Downloads estar sempre cheia enquanto Documentos, Imagens, Vídeos e as outras pastas da home vazias.

Isso me deu uma ótima ideia: fazer um script para mover automaticamente os arquivos da pasta de downloads para a sua pasta correta na home afinal, elas existem para isso de acordo com a extenção do arquivo.

Bill Gates sobre pessoas preguiçosas

O que exatamente será feito?

  1. Vamos criar um arquivo de configuração onde teremos o caminho da pasta e seus tipos de arquivos
  2. Vamos classificar cada arquivo de acordo com a sua extenção
  3. Vamos mover esse arquivo para a sua determinada pasta
  4. Fazer com que o script seja executado automaticamente

Criando o arquivo de configuração.

No Linux nós temos muitos arquivos e pastas especiais. Se você usa um terminal bash certamente já ouviu falar sobre o arquivo .bashrc, para zsh é o .zshrc e por aí vai. Mas se você utiliza fish assim como eu, já deve estar familiarizado com a pasta .config.

Para quem não entendeu nada do que foi dito anteriormente Arquivos e pastas que iniciam com um . são pastas e arquivos ocultos.

A home no linux é uma pasta especial com o caminho /home/username que é o equivalente à pasta C:\\users\\username no windows.

Pegando as pastas do usuário

As pastas que estão em /home/user são geradas automaticamente no primeiro login e depois são atualizadas de acordo com o arquivo ~/.config/user-dirs.dirs que pode ser editado diretamente ou traduzido de acordo com o locale definido em ~/.config/user-dirs.conf. Existe um script que é rodado cada vez que você faz login no sistema para verificar possíveis mudanças nesses arquivos chamado xdg-user-dirs-update.

A melhor forma de conseguir pegar as pastas é utilizando o comando xdg-user-dir que retorna o caminho /home/username quando chamado sem nenhum argumento. Esse comando pode receber um parâmetro chamado NAME que tem 8 valores possíveis:

DESKTOP, DOWNLOAD, TEMPLATES, PUBLICSHARE, DOCUMENTS, MUSIC, PICTURES, VIDEOS

Para cada um desses argumentos é retornado o caminho completo até a respectiva pasta. Por exemplo:

[user]$ xdg-user-dir
>> /home/user

[user]$ xdg-user-dir DOCUMENTS # Em inglês
>> /home/user/documents

[user]$ xdg-user-dir DESKTOP # Em português
>> /home/user/'Área de trabalho'

A partir desse utilitário podemos garantir com que pegamos as pastas corretas além de que elas existem, o que irá evitar muitas dores de cabeça no futuro.

Eu fiz esse script na linguagem Python, mas você pode fazer o mesmo programa em absolutamente qualquer linguagem de programação, desde shell script até C. Linguagens são apenas ferramentas então é a parte menos importante. O mesmo serve para o arquivo de configuração, eu preferi utilizar JSON, mas você pode utilizar qualquer um de sua preferência, desde um csv até xml.

Para gerar esse arquivo eu fiz da seguinte forma:

# setup.py

import json
import os
from subprocess import check_output

USER_DIRS = [
      check_output(["xdg-user-dir", "DOCUMENTS"])[:-1].decode("utf-8"),
    check_output(["xdg-user-dir", "PICTURES"])[:-1].decode("utf-8"),
    check_output(["xdg-user-dir", "MUSIC"])[:-1].decode("utf-8"),
    check_output(["xdg-user-dir", "VIDEOS"])[:-1].decode("utf-8"),
]

FILE_EXTENSIONS = [
      ["pdf", "odt", "doc", "docx", "csv", "ods", "xls", "xlsx"],
    ["png", "jpeg", "jpg", "gif", "webp", "svg"],
    ["mp3", "wav", "ogg"],
    ["mp4", "avi", "wmv", "mov", "flv", "mkv", "webm"],
]


def setup():
    HOME = check_output(["xdg-user-dir"])[:-1].decode("utf-8")
    CONFIG_PATH = f"{HOME}/.config/downloads-organizer"

    default_json = dict(zip(USER_DIRS, FILE_EXTENSIONS))

    config_json = json.dumps(default_json, indent=2)

    if not os.path.exists(CONFIG_PATH):
        os.mkdir(CONFIG_PATH)

    if not os.path.isfile(f"{CONFIG_PATH}/config.json"):
        with open(f"{CONFIG_PATH}/config.json", "w", encoding="utf-8") as config_file:
            config_file.write(config_json)

O algoritmo é muito simples:

  1. Criamos uma lista com as pastas do usuário, utilizando o utilitário xdg-user-dir
  2. Criamos uma lista com as extenções de arquivos para cada pasta
  3. Juntamos as duas listas com zip() e transformamos em um dicionário com dict()
  4. Transformamos o dicionário em um JSON
  5. Verificamos se a pasta ~/.config/downloads-organizer existe e, caso não exista, criamos ela
  6. Fazemos a mesma verificação com o arquivo ~/.config/downloads-organizer/config.json e, caso não exista, o criamos.

Depois de rodarmos essa função termos um arquivo de configuração para o nosso script salvo em ~/.config/downloads-organizer. Nesse caso eu preferi fazer em JSON, mas poderia ser absolutamente qualquer outro formato.

Classificando os arquivos

Essa é provavelmente a parte mais simples do programa, basicamente vamos precisar pegar a extenção do arquivo e utilizando o arquivo de configuração criado verificar se ela se encaixa em alguma das pastas.

# classify_file.py

from json import load
from subprocess import check_output

HOME = check_output(["xdg-user-dir"])[:-1].decode("utf-8")


def classify_file(file: str):
    file_extension = file.split(".")[-1]
    with open(
          f"{HOME}/.config/downloads-organizer/config.json", "r", encoding="utf-8"
    ) as config_file:
        config = load(config_file)
        for folder in config:
            if file_extension in config[folder]:
                return folder
        return check_output(["xdg-user-dir", "DESKTOP"])[0:-1].decode("utf-8")

Como eu disse, o algoritmo é muito simples:

  1. Separa o nome do arquivo da sua extenção com split(".") e armazena apenas a extenção
  2. Abre o arquivo de configuração e armazena como um dicionário python
  3. Para cada pasta no arquivo de configuração verifica se a extenção do arquivo deve estar na pasta, caso esteja retorna o caminho para a pasta
  4. Caso o valor não se encaixe retorna uma pasta padrão (no meu caso é a pasta Desktop)

Movendo os arquivos

Aqui cabe ressaltar o que significa objetivamente mover um arquivo. Normalmente nós fazemos isso apenas por interface gráfica, mas também é possível via linha de comando, que é o que nos mostra o que significa mover um arquivo. Em linha de comando nós temos um comando chamado mv que move os arquivos, mas também serve para renomear os arquivos. Isso significa que mover é basicamente renomear o arquivo.

No Python existem várias maneiras para fazer isso, principalmente as seguintes:

import shutil
import os
import subprocess

shutil.move('caminho/do/arquivo', 'novo/caminho/do/arquivo')
os.rename('caminho/do/arquivo', 'novo/caminho/do/arquivo')
subprocess.run('mv', 'caminho/do/arquivo', 'novo/caminho/do/arquivo')

Independentemente da forma escolhida para mover o arquivo, o resultado será o mesmo. Nesse caso:

# move_file.py

import os

HOME = os.environ.get("HOME")


def move(file, folder):
    new_file_name = file
    i = 1
    while os.path.exists(f"{folder}/{new_file_name}"):
        new_file_name = f"[{i}]{file}"
        i += 1
    os.rename(f"{HOME}/Downloads/{file}", f"{folder}/{new_file_name}")

O algoritmo é o seguinte:

  1. verifica se o nome do arquivo já existe e, caso já exista, dá um índice antes do nome do arquivo
  2. move o arquivo para a pasta

main.py

Por fim, juntando todas as funções teremos o nosso script completo:

# main.py

import os

from classify_file import classify_file
from move_file import move
from setup import setup

print("Start oganizing Downloads files")

HOME = os.environ.get("HOME")

setup()

files = [
      f
    for f in os.listdir(f"{HOME}/Downloads")
    if os.path.isfile(f"{HOME}/Downloads/{f}")
]

for file in files:
    folder = classify_file(file)
    move(file, folder)

print("Done oganizing Downloads files")

Fazendo o script rodar automaticamente

Cada usuário tem uma pasta que define algums programas que serão iniciados de forma automática cada vez que o usuário faz login. Essa pasta é a ~/.config/autostart e cada programa deve ter um arquivo desktop para ser rodado. Basicamente ao invés de colocar o seu programa inteiro dentro dessa pasta você precisa de apenas um arquivo que contém alguns metadados e representa alguma rotina, mais ou menos como funcionam os atalhos do windows.

Por exemplo, eu utilizo o cliente de emails Geary que tem a funcionalidade de notificar todos os emails que eu recebo, portanto ele é iniciado sempre que faço login com o meu usuário. O seu arquivo desktop é o seguinte:

# org.gnome.Geary.desktop

[Desktop Entry]
Type=Application
Name=org.gnome.Geary
Exec=flatpak run --command=geary org.gnome.Geary --gapplication-service
X-Flatpak=org.gnome.Geary

Como pode ver é um arquivo com metadados e o mais importante é o Exec, pois a partir dele podemos rodar qualquer script a partir desse arquivo. Vamos fazer um arquivo chamado downloads-organizer.desktop na pasta ~/.config/autostart:

# ~/.config/autostart/downloads-organizer.desktop

[Desktop Entry]
Type=Application
Name=downloads organizer
Exec=python3 caminho/para/o/arquivo/main.py
Terminal=false

E pronto! Agora cada vez que o usuário fizer login o script será rodado automaticamente.

Conclusão

O script que eu apresentei aqui é muito simples, mas não deixa de ser muito útil no nosso dia a dia. Se você chegou até aqui deve ter percebido que em nenhum momento eu fiz um tutorial de um programa Python, veja isso como uma oportunidade de fazer esse mesmo script na linguagem de sua preferência, garanto que será uma ótima experiência para aprender mais sobre a sua linguagem preferida.

Repositório do projeto

Carregando publicação patrocinada...
1

Amigo, muito legal seu script! Sou usario Arch também e é real essa estranheza, tudo acaba ficando em Downloads kkk mas já era assim no Ubuntu, e no Kali, backbox...

Mas gostei muito da sua solução :) meus parabéns. Otimo conteúdo.

1

parabéns pela iniciativa do projeto. Eu enfrentava o mesmos problemae e eu fiz um programa que organizar minha pasta de download assim que um arquivo novo aparece.

se quiser da uma olhada no projeto:repositório

1

É muito legal saber que existem pessoas que tiveram essa mesma ideia, ano passado desenvolvi uma solução parecida pro macOS usando Swift. Parabéns pelo trabalho e obrigado por ter escrito esse artigo compartilhando isso com a comunidade.

1

Fazendo o script rodar automaticamente

Pode usar o cron ou systemd/Timers.

sendo mais preguiçoso uso o cron pra rodar um shellscript que faz isso aí basicamente, por exemplo:

sortinshit.sh:

mv *.pdf *.odt *.csv ~/Documents/misc/
mv *.jpeg *.png ~/Images/

aí é setar como executável

sudo chmod +x sortinshit.sh

e botar no cron tab:

0 12 * * * ./caminho/do/script/sortinshit.sh

vai executar 12hr todo dia.