Olá, ions por aqui, novamente.
O medo de estar aprendendo algo que um dia uma IA irá realizar, me deixa completamente angustiado. Mas, se "resolver problemas" ainda for uma exigência impostas aos seres humanos do futuro, por que não persistir?
Desta vez trago outro tutorial. Menos inútil que o primeiro. Então vamos definir as estruturas dos "problemas", pois de uma coisa nós já sabemos: quem não tem problemas, é porque não procurou o suficiente. E quem ainda não os encontrou, é só uma questão de tempo para conseguir criá-los.
Estrutura do projeto
A estrutura mais simples do programa é:
- Varrer uma pasta (por exemplo a pasta de downloads ou outro diretório)
- Identificar o tipo de cada arquivo no diretório em questão
- Mover o arquivo para uma subpasta correspondente ao seu tipo (imagens, videos, documentos...)
Iniciando o projeto
Crie um diretório e navegue até ele:
mkdir organizador
cd organizador
Crie um arquivo organizador.go e inicie os módulos do mesmo:
touch organizador.go
go mod init organizador.go
Você deve ter algo mais ou menos assim:
~/organizador
.
├── go.mod
└── organizador.go
Parte 1: Verificando se um diretório existe
Vamos definir o diretório de origemdirOrigem
no qual realizaremos a organização. Com ele definido, vamos verificar se ele existe de fato, caso contrário, retornaremos um erro:
package main
import (
"fmt"
"os"
)
// Defina o deretório o qual você quer organizar como variável global
var dirOrigem string := "/Users/User/Downloads" // Troque o diretório
func main() {
// Verificar se o diretório existe, caso contrário, retornar erro
if _, err := os.Stat(dirOrigem); os.IsNotExist(err) {
fmt.Println("BAD DIR :( \nDiretório não encontrado: ", dirOrigem)
return
} else {
// Imprimir mensagem caso o diretório exista
fmt.Println("GOOD DIR :) \nDiretório encontrado: ", dirOrigem)
}
}
Agora, vamo fazer algumas considerações a respeito do código acima:
- A função
os.Stat
retorna dois valores do tipoos.FileInfo
e erroerr
.
-
FileInfo
é uma interface que retorna informações detalhadas de arquivos, que neste caso, não precisamos. Portanto, para ignorarmos essa interface, utilizamos_,
. Entretanto, não queremos ignorar oerro: if _, err := ...
- Passamos o erro
err
como parâmetro para a funçãoos.IsNotExist()
, pois se o diretório não existir, a funçãoos.Stat()
retornará um erro NÃO NULO, fazendo com que a funçãoos.IsNotExist()
retornetrue
, executando nossa mensagem:BAD DIR :(
- Se
os.IsNotExist()
retornarfalse
, teremos a impressão da mensagem da condiçãoelse
:GOOD DIR :)
Parte 2: Conceito de função de "callback" é uma doidera, bixo!
Você reparou que aqui estamos indo aos poucos e nos deliciando com os bits e bytes ao som do teclado mecânico. _Tchaka tchaka bum! _
E agora nos vamos criar uma função do tipo callback. Algo que eu nunca tinha de fato aprendido sobre, ou nunca tive curiosidade o suficiente para questionar se um dia eu utilizei esse conceito em algum código python da minha vida pré-golang.
Função callback é uma função passada com argumento para outra função.
Se você já está familiarizado com o conceito, congrats, caso constrário, congrats. Ou seja, congrats!
Agora vamos criar uma função callback listarArquivos
que será passada como argumento para outra função filepath.Walk
.
func main() {
// Restante do código
.
.
.
// Percorrer e listar os arquivos no diretório dirOrigem
err := filepath.Walk(dirOrigem, listarArquivos)
if err != nil {
fmt.Println("Erro ao percorrer o diretório: ", err)
}
}
// Função que lista os arquivos do diretório
func listarArquivos(caminho string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Ignorar diretórios e exibir apenas arquivos
if !info.IsDir() && !strings.HasPrefix(info.Name(), ".") {
fmt.Println("Arquivo encontrado: ", info.Name())
}
return nil
}
Mas calma, como que filepath.Walk
chama a Função de Callback?
Quando você chama filepath.Walk(dirOrigem, listarArquivos)
, a função filepath.Walk
faz o trabalho pesado de percorrer todos os arquivos e subdiretórios dentro de dirOrigem
.
Para cada arquivo ou diretório encontrado, ela chama a função listarArquivos
com três argumentos:
- caminho: o caminho completo para o arquivo ou diretório atual.
- info: um objeto
os.FileInfo
que contém informações sobre o arquivo/diretório (como nome, se é um diretório, tamanho, etc.). - err: um erro, caso algo dê errado ao acessar esse arquivo ou diretório.
O Go entende automaticamente que listarArquivos
deve receber esses três parâmetros porque filepath.Walk
espera uma função que siga exatamente essa assinatura:
// Função Walk()
func Walk(root string, walkFn WalkFunc) error
// Tipo WalkFunc
type WalkFunc func(path string, info os.FileInfo, err error) error
Repare que a função Walk
retorna um error! Isso é relevante!
Por isso, que igualamos a nossa função filepath.Walk(dirOrigem, listarArquivos)
a um err
:
err := filepath.Walk(dirOrgiem, listarArquivos)
Pois no final das contas, por retornar um erro, ela é um erro XD
Exemplo em Ação
Aqui está uma visão mais detalhada do que acontece em cada passo:
//Percorrer e listar os arquivos no diretório
err := filepath.Walk(dirOrigem, listarArquivos)
Para cada arquivo ou diretório em dirOrigem
, filepath.Walk
vai chamar listarArquivos
como se fosse algo assim:
listarArquivos("/Users/User/Downloads/file1.txt", infoSobreFile1, nil)
listarArquivos("/Users/User/Downloads/file2.txt", infoSobreFile2, nil)
listarArquivos("/Users/User/Downloads/file3.mp3", infoSobreFile3, nil)
Nesse exemplo, para cada chamada:
- caminho: recebe o caminho do arquivo ou diretório.
- info: contém informações sobre esse item (como o nome e o tipo).
- err: é usado para capturar qualquer erro específico ao acessar o arquivo/diretório.
Função de Callback
A listarArquivos
é uma função de callback que filepath.Walk
chama automaticamente com esses valores. Assim, não precisamos nos preocupar em definir os valores de caminho
, info
e err
; o filepath.Walk
já faz isso por nós.
UFA!
Agora faça aquele teste maroto em seu terminal:
go run organizador.go
Você pode ter com resultado:
GOOD DIR :)
Diretório encontrado: /Users/User/Downloads
Arquivo encontrado: Suic_ SOS · 4.32pm · 10-25.jpeg
Arquivo encontrado: Suic_ SOS.jpeg
Arquivo encontrado: quantum-distortion-cyberpunk-music-234058.mp3
Arquivo encontrado: slow-mode-enabled-cyberpunk-music-230623.mp3
Arquivo encontrado: teaching-lp-20152-seminario-go.pdf
Ou:
go run organizador.go
BAD DIR :(
Diretório não encontrado: /Users/User/Downloadss
Nesse caso eu só coloquei um "s" extra em "Downloads" para que o dirOrigem
ficasse incorreto.
Agora apague a função listarArquivos
, pois não vamos usá-la.
Brincadeira, só vamos mudar ela de nome e adicionar outras lógicas.
PARTE 3: ORGANIZAR != ORGANIZADO
Organizado é bom, organizar é fod@.
Depois desta constatação genial da minha parte, vamos partir para a parte que nos interessa de fato: organizar a p*rr@ toda.
Por ironia da vida, antes de organizar os arquivos, temos que organizar as idéias dos próximos passos.
Nossa próxima função precisa, basicamente:
- Criar subpastas com base nas extensões de cada arquivo do nosso diretório dirOrigem, caso ela não exista.
- Mover os arquivos para suas respectivas pastas de acordo com suas extensões.
- Mas se os arquivos já estiverem nas subpastas organizadoras, ele não deve criá-las novamente.
Vamos entender o que cada parte desse código faz:
// Função que organiza os arquivos do diretório
func organizarArquivos(caminho string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Ignorar diretórios e exibir apenas arquivos
if !info.IsDir() && !strings.HasPrefix(info.Name(), ".") {
fmt.Println("Arquivo encontrado: ", info.Name())
// Obter extensões em letras minúsculas
extensao := strings.ToLower(filepath.Ext(info.Name()))
// Criar nome das subpastas diretamente em dirOrigem
subpasta := filepath.Join(dirOrigem, extensao[1:])
// Verificar se a subpasta já existe; caso contrário, criar a pasta
if _, err := os.Stat(subpasta); os.IsNotExist(err) {
err := os.Mkdir(subpasta, os.ModePerm)
if err != nil {
fmt.Println("Erro ao criar subpasta: ", err)
return err
}
}
// Caminho de destino para mover arquivos
caminhoDestino := filepath.Join(subpasta, info.Name())
// Verificar se o arquivo já está na subpasta
if caminhoDestino == caminho {
fmt.Printf("O arquivo %s já está na subpasta %s. Ignorando...\n", info.Name(), subpasta)
} else {
// Mover o arquivo para a subpasta
err := os.Rename(caminho, caminhoDestino)
if err != nil {
fmt.Println("Erro ao mover arquivo: ", err)
return err
}
fmt.Printf("Arquivo %s movido para %s\n", info.Name(), subpasta)
}
}
return nil
}
Estrutura da função organizarArquivos
A função organizarArquivos
é chamada para cada arquivo ou pasta encontrada na estrutura de diretórios. Ela verifica as condições para organizar cada arquivo com base em sua extensão, criando subpastas e movendo arquivos, se necessário.
func organizarArquivos(caminho string, info os.FileInfo, err error) error {
if err != nil {
return err
}
Aqui, a função organizarArquivos
recebe três parâmetros:
-
caminho
: o caminho completo para o arquivo ou diretório atual. -
info
: informações do arquivo ou diretório, obtidas do tipoos.FileInfo
. -
err
: um possível erro que pode ocorrer ao tentar acessar o item.
A primeira verificação é se existe algum erro ao acessar o arquivo/diretório. Caso positivo, ele é retornado imediatamente.
Filtrando arquivos e ignorando diretórios ocultos
if !info.IsDir() && !strings.HasPrefix(info.Name(), ".") {
fmt.Println("Arquivo encontrado: ", info.Name())
Este trecho faz duas verificações:
-
!info.IsDir()
: verifica se o item não é um diretório (ou seja, é um arquivo). -
!strings.HasPrefix(info.Name(), ".")
: verifica se o nome do arquivo não começa com ".", ignorando arquivos ocultos em sistemas baseados em Unix.
Se as duas condições forem atendidas, o arquivo é exibido com fmt.Println
.
Identificando a extensão do arquivo e criando o nome da subpasta
extensao := strings.ToLower(filepath.Ext(info.Name()))
subpasta := filepath.Join(dirOrigem, extensao[1:])
Aqui:
-
strings.ToLower(filepath.Ext(info.Name()))
: extrai a extensão do arquivo (por exemplo,.txt
) e a transforma em minúsculas para garantir consistência. -
subpasta := filepath.Join(dirOrigem, extensao[1:])
: cria o caminho completo da subpasta onde o arquivo será movido. Oextensao[1:]
remove o ponto inicial (.
) da extensão, formando o nome da subpasta, comotxt
.
Criando a subpasta, caso ainda não exista
if _, err := os.Stat(subpasta); os.IsNotExist(err) {
err := os.Mkdir(subpasta, os.ModePerm)
if err != nil {
fmt.Println("Erro ao criar subpasta: ", err)
return err
}
}
Aqui, a função:
- Verifica se a subpasta já existe usando
os.Stat
. - Se a subpasta não existe (
os.IsNotExist(err)
), ela é criada comos.Mkdir(subpasta, os.ModePerm)
. -
os.ModePerm
define as permissões padrão para a nova pasta. Caso haja um erro ao criar a pasta, ele é exibido e retornado.
Definindo o caminho de destino do arquivo
caminhoDestino := filepath.Join(subpasta, info.Name())
Neste ponto, caminhoDestino
representa o caminho final para onde o arquivo será movido. Ele é construído usando filepath.Join
, para unir o caminho da subpasta ao nome do arquivo.
Verificando se o arquivo já está na pasta de destino
if caminhoDestino == caminho {
fmt.Printf("O arquivo %s já está na subpasta %s. Ignorando...\n", info.Name(), subpasta)
} else {
// Mover o arquivo para a subpasta
err := os.Rename(caminho, caminhoDestino)
if err != nil {
fmt.Println("Erro ao mover arquivo: ", err)
return err
}
fmt.Printf("Arquivo %s movido para %s\n", info.Name(), subpasta)
}
}
return nil
}
- Este trecho compara o
caminhoDestino
com ocaminho
atual do arquivo. Se forem iguais, significa que o arquivo já está na subpasta correta, então ele é ignorado com uma mensagem (fmt.Printf
). - Caso contrário,
os.Rename(caminho, caminhoDestino)
move o arquivo para a subpasta. Se houver erro durante a movimentação, ele é retornado.
Resumo Final
A função:
- Percorre um diretório, verificando cada item.
- Ignora diretórios e arquivos ocultos.
- Determina a extensão do arquivo e, com isso, a subpasta de destino.
- Cria a subpasta (se ainda não existir).
- Move o arquivo para a subpasta, a menos que ele já esteja lá.
O uso de filepath.Walk(dirOrigem, organizarArquivos)
passa essa função para cada arquivo dentro do diretório, fazendo com que todos sejam organizados automaticamente.
Esse código se encaixa bem como uma função de organização de arquivos porque ele lida com a lógica de criação e movimentação em uma única função – uma forma eficiente e organizada de estrutura.
REPO: https://github.com/ionnss/organizador
***Mais um dia na terra,
ions