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

☕ Java - Fazer seu programa de prompt rodar a partir de qualquer diretório

Vou usar um programa que fiz para me auxiliar no trabalho como exemplo.

Explicando o meu programa

Tenho vários arquivos com esse formato de nome. Quero uma pasta para cada código conforme o nome dos arquivos, e que eles fiquem armazenados nelas.

VRJ_018_OPERADOR.sql
VRJ_018_AUXILIAR.sql
VRJ_018_CONTROLE.sql
VRJ_018_NEGOCIO.sql
PROCESS_042_OPERADOR.sql
PROCESS_042_AUXILIAR.sql
PROCESS_042_CONTROLE.sql
PROCESS_042_NEGOCIO.sql
ATIVO_030_OPERADOR.sql
ATIVO_030_AUXILIAR.sql
ATIVO_030_CONTROLE.sql
ATIVO_030_NEGOCIO.sql
...

Objetivo

Escolha uma pasta ou crie uma para armazenar seu softwares em Java que trarão produtividade para o seu prompt, Ex: C:\JAVA_LIB
Eu acreditava que era possível colocar apenas o .java na pasta e usar, pensava que ao chamar

java SeparaArquivos.java

tudo iria funcionar.

Não dá certo, tem que converter para bytecodes, gerando o arquivo .class
Então dentro da pasta C:\JAVA_LIB se ainda não possui o .class, dê o comando

javac SeparaArquivos.java

Será gerado um arquivo com a extensão .class, no meu caso em particular
SeparaArquivos.class

Inclua na variável de ambiente CLASSPATH o caminho C:\JAVA_LIB

Basta chamar

java SeparaArquivos

Pronto! Chame seu programa Java a partir de qualquer diretório sempre que precisar processar algo.
Claro, o seu Java deve estar instalado corretamente.
Java roda em Linux, as possibilidades são infinitas.
Java é mais poderoso que um arquivo .bat e este não é compatível com Linux.
E eu particularmente acho o código Java muito mais fácil que o de um .bat

Fonte do programa

import java.io.*;
import java.nio.file.*;
import java.util.regex.*;

public class SeparaArquivos {
    public static void main(String[] args) {
        String pastaOrigem = System.getProperty("user.dir");
        String pastaDestino = System.getProperty("user.dir");

        moverArquivos(pastaOrigem, pastaDestino);
    }

    public static void moverArquivos(String pastaOrigem, String pastaDestino) {
        File diretorioOrigem = new File(pastaOrigem);
        File[] arquivos = diretorioOrigem.listFiles();

        if (arquivos != null) {
            for (File arquivo : arquivos) {
                if (arquivo.isFile() && arquivo.getName().toLowerCase().endsWith(".sql") ) {
                    String nomeArquivo = arquivo.getName();
                    String numero = extrairNumero(nomeArquivo);

                    if (numero != null) {
                        File novaPasta = new File(pastaDestino + File.separator + numero);
                        if (!novaPasta.exists()) {
                            novaPasta.mkdirs();
                        }

                        try {
                            Path origem = arquivo.toPath();
                            Path destino = new File(novaPasta.getPath() + File.separator + nomeArquivo).toPath();
                            Files.move(origem, destino, StandardCopyOption.REPLACE_EXISTING);
                            System.out.println("Arquivo " + nomeArquivo + " movido para a pasta " + numero);
                        } catch (IOException e) {
                            System.out.println("Erro ao mover o arquivo " + nomeArquivo);
                        }
                    }
                }
            }
        }
    }
    
    public static String extrairNumero(String nomeArquivo) {
        String numero = null;
        Pattern pattern = Pattern.compile("_(\\d+)_");
        Matcher matcher = pattern.matcher(nomeArquivo);
       
        if (matcher.find()) {
            numero = matcher.group(1);
        }
       
        return numero;
    }

}

Esse código dá para melhorar muito, está assim porque pedi o auxílio do ChatGPT.
Meu foco era organizar os arquivos o mais rápido possível e não criar o melhor separador de arquivos do mundo.

Um simples exemplo if(arquivos != null) não precisa aninhar.
Eu já daria um return if(arquivos == null){ return }

Carregando publicação patrocinada...
1
1

O comando java SeparaArquivos.java funciona somente a partir do Java 11.

A diferença é que ele roda o arquivo diretamente, sem gerar o respectivo .class.

1

A JDK instalada no meu PC é a versão 20.
Sim, com o arquivo presente na pasta, eu consegui rodar assim

java SeparaArquivos.java

Então pensei: Poxa, toda vez que eu precisar separar meus arquivos em pastas automaticamente, terei que colar o programa SeparaArquivos.java nela e rodá-lo.
Então aconteceu o que mencionei no post.
Apenas joguei ele no diretório de CLASSPATH e não deu certo.
Só funcionou após eu gerar o .class

2

Ah sim, agora entendi o que não funcionou. Bom, quando vc roda java Arquivo.java, ele ainda vai ter que compilar o arquivo, então precisa passar o caminho completo dele (por isso só funciona se ele está na mesma pasta). Só que como ele não cria o .class (pois o bytecode fica só em memória), então não adianta setar o classpath.

Gerando o .class funciona porque o classpath indica onde procurá-lo, e aí ele sempre encontrará, independente de onde vc executar.


Aproveitando, umas sugestões de melhoria.

Vc não precisa compilar a regex toda hora. Pode deixar o Pattern como uma variável estática (só é carregada uma vez), assim a regex não será compilada toda vez que o método for chamado:

public class SeparaArquivos {
    // só compila a regex uma vez
    private static final Pattern EXTRAI_NUMERO = Pattern.compile("_(\\d+)_");

    public static String extrairNumero(String nomeArquivo) {
        Matcher matcher = EXTRAI_NUMERO.matcher(nomeArquivo);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    // demais métodos...
}

E para listar os arquivos, basta passar um java.io.FileFilter, que aí vc nem precisa verificar nada dentro do for, pois ele já trará somente os arquivos que vc quer:

public static void moverArquivos(String pastaOrigem, String pastaDestino) {
    File diretorioOrigem = new File(pastaOrigem);
    // listFiles já pode filtrar os arquivos diretamente
    for (File arquivo : diretorioOrigem.listFiles((f) -> f.isFile() && f.getName().toLowerCase().endsWith(".sql"))) {
        String nomeArquivo = arquivo.getName();
        String numero = extrairNumero(nomeArquivo);
        if (numero != null) {
            File novaPasta = new File(pastaDestino, numero);
            if (!novaPasta.exists()) {
                novaPasta.mkdirs();
            }

            try {
                Path origem = arquivo.toPath();
                Path destino = new File(novaPasta.getPath(), nomeArquivo).toPath();
                Files.move(origem, destino, StandardCopyOption.REPLACE_EXISTING);
                System.out.println("Arquivo " + nomeArquivo + " movido para a pasta " + numero);
            } catch (IOException e) {
                System.out.println("Erro ao mover o arquivo " + nomeArquivo);
            }
        }
    }
}

Repare também que o construtor de File pode receber o diretório pai e o nome do filho (seja um subdiretório, seja um arquivo), portanto mudei para new File(pastaDestino, numero) e new File(novaPasta.getPath(), nomeArquivo) (não precisa concatenar com o File.separator).


Mas já que vc está usando java.nio, por que não usar para tudo? No caso, só vai precisar desses import's:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

E o método moverArquivos fica assim:

public static void moverArquivos(String pastaOrigem, String pastaDestino) throws IOException {
    Path origem = Paths.get(pastaOrigem);
    Path destino = Paths.get(pastaDestino);
    try (Stream<Path> files = Files.list(origem)) {
        files // filtra os arquivos .sql
            .filter(p -> Files.isRegularFile(p) && p.toString().toLowerCase().endsWith(".sql"))
            // processa cada um
            .forEach(sqlFile -> {
                String nomeArquivo = sqlFile.getFileName().toString();
                String numero = extrairNumero(nomeArquivo);
                if (numero != null) {
                    try {
                        Path novaPasta = Paths.get(destino.toString(), numero);
                        Files.createDirectories(novaPasta); // só cria se não existir
                        Files.move(sqlFile, Paths.get(novaPasta.toString(), nomeArquivo), StandardCopyOption.REPLACE_EXISTING);
                        System.out.println("Arquivo " + nomeArquivo + " movido para a pasta " + numero);
                    } catch (IOException e) {
                        System.out.println("Erro ao mover o arquivo " + nomeArquivo);
                    }
                }
            });
    }
}

Outra diferença importante é que File.listFiles primeiro lê todas as entradas do diretório, e só depois retorna o array. Já Files.list retorna uma stream que é populada de maneira lazy (seria "sob demanda", apesar de não ser exatamente assim e ser dependente da implementação). Isso faz diferença se o diretório tiver muitos arquivos, por exemplo (o primeiro precisa ler tudo pra gerar o array, o segundo processa um de cada vez e em seguida descarta, não precisando gerar o array com tudo).

1

Sim, também dá para receber que tipo de arquivo você quer filtrar nos argumentos.
Parabéns, por revisar, como eu havia dito as possibilidades são infinitas.