Há um tempo atrás eu havia criado um script em Bash(Shell Script que limpava algumas coisas no meu sistema, dentre elas:
O script funcionava normalmente, bastava eu rodar o comando limpeza
no terminal.
No entanto, um belo dia quando rodei esse script, notei que ele demorou muito de executar. O motivo disso foi que na lixeira havia diversos arquivos .iso
, somados acho que havia uns 5GB de tamanho!
E então eu pensei perguntando pra mim mesmo:
— Por que o Bash demorou tanto para remover???
E pensei:
— Se eu fizesse esse script em C++, será que demoraria tanto?!
Então, eu decidi fazer e testar! Após terminar de escrever, deletei uns 10GB de .iso
(joguei na lixeira) e rodei o código pós-compilado pra testar com o comando time e comparei o resultado do mesmo comando com o do script em Bash.
E a diferença foi GRANDEEEE! Ou seja, o mesmo script feito em C++ foi muito mais rápido!
Bom, isso já faz muito tempo, mas decidi postar aqui, pois esse “script” serve de exercício para quem está treinando seus códigos C++, é uma boa você usar: C++ como Shell Script para aprimorar suas habilidades!
Nos próximos tópicos vou descrever cada linha/trecho do código e ao final haverá todos os arquivos separados para caso você queira compilar e testar na sua máquina.
Como vamos compilar com CMake, o ideal é criar uma pasta para o projeto:
mkdir cleanup
cd cleanup
E então crie esse primeiro arquivo: vim limpeza.hpp
:
O cabeçalho local:
colors.hpp
, pode ser obtido da postagem: Crie sua própria biblioteca de cores para C++. Acesse, copie o código que tem lá e salve com esse mesmo nome.
Vamos adicionar:
#pragma once
#include <iostream>
#include <filesystem>
#include <memory>
#include <array>
#include <fstream>
#include "colors.hpp"
Pra não estender muito o nome do namespace
namespace fs = std::filesystem;
Nome da classe, bem intuitivo! =)
class Limpeza
Caso o usuário queira executar o código sem alterar/remover nada no seu PC, além de exibir o(a)s pastas/arquivos que serão removidos:
bool m_debug;
Armazena os caminhos dinamicamente:
std::string m_path;
Capta a pasta pessoal do usuário:
const std::string m_home = std::getenv("HOME");
Tudo aquilo que iremos limpar, que falei no início:
const fs::path m_trash = ".local/share/Trash/files";
const fs::path m_thumbs = ".cache/thumbnails";
const fs::path m_chrome = ".cache/google-chrome";
const fs::path m_fox = ".cache/mozilla";
const fs::path m_opera = ".cache/opera";
const fs::path m_recent = ".local/share/recently-used.xbel";
const fs::path m_bash_h = ".bash_history";
Essas funções-membro declarei como: protected
:
void print(bool, const std::string&);
Recebe um array opcional com: caminho, mensagem true
e mensagem false
. Essas duas funções-membro fazem quase a mesma coisa, sendo que uma é para pastas e a outra para arquivos regulares
:
bool clean_dir(const std::array<std::string, 3>& = {});
bool clean_file(const std::array<std::string, 3>& = {});
E como public
só declarei o construtor e run()
:
Limpeza(bool);
void run(); // Para ser chamada na main
Após isso criei a execução do código no arquivo: vim limpeza.cpp
:
#include "limpeza.hpp"
C++ não precisa de getters
e setters
sempre, como em Java. Você já pode(e deve) inicializar os membros com essa sintaxe no construtor. Nesse caso, inicializamos o m_debug
e a m_path
:
Limpeza::Limpeza(bool debug) : m_debug(debug){
m_path = {};
}
Essa função-membro chama as outras funções-membro e se o modo debug
estiver ativado ela exibe o caminho, para você ter certeza que não deletará nada que não seja os arquivos/pastas especificados!
void Limpeza::run(){
this->clean_dir({m_trash, "Lixeira esvaziada", "esvaziar lixeira"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_file({m_recent, "Arquivos recentes limpados", "limpar arquivos recentes"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_file({m_bash_h, "Histórico do Bash removido", "limpar histórico do Bash"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_thumbs, "Thumbnails removidas", "remover cache das Thumbnails"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_chrome, "Cache do Chrome limpado", "remover cache do Chrome"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_fox, "Cache do Firefox limpado", "remover cache do Firefox"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_opera, "Cache do Opera limpado", "remover cache do Opera"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
}
Essa print
, organiza a saída para não ficar repetindo sempre:
void Limpeza::print(bool action, const std::string& msg){
(action) ?
std::cout << hey::green + "\u2705 " + msg + " com sucesso!" + hey::off << '\n' :
std::cerr << hey::red + "\u274C Falha ao " + msg + "." + hey::off << '\n';
}
Remove os diretórios:
bool Limpeza::clean_dir(const std::array<std::string, 3>& arr){
m_path = m_home + '/' + arr[0];
if(m_debug){
this->print(true, arr[1]);
return true;
}
try {
if (fs::exists(m_path) && fs::is_directory(m_path)) {
if (fs::is_empty(m_path)) {
return false;
}else{
if(fs::remove_all(m_path)){
this->print(true, arr[1]);
fs::create_directory(m_path);
}else{
this->print(false, arr[2]);
return false;
}
}
}else{
return false;
}
}catch (const fs::filesystem_error& e){
std::cerr << "EXECUTAR ESSA AÇÃO: " << e.what() << '\n';
return false;
}
return true;
}
As mesmas ações da função-membro acima, mas para arquivos regulares
e usa a std::ofstream
em vez da std::filesystem
para recriar os arquivos e não pastas:
bool Limpeza::clean_file(const std::array<std::string, 3>& arr){
m_path = m_home + '/' + arr[0];
if(m_debug){
this->print(true, arr[1]);
return true;
}
try {
if (fs::exists(m_path) && fs::is_regular_file(m_path)) {
std::size_t size = std::filesystem::file_size(m_path);
if(size == 0){
return false;
}
if(fs::remove_all(m_path)){
this->print(true, arr[1]);
std::ofstream out(m_path);
out.close();
}else{
this->print(false, arr[2]);
return false;
}
}else{
return false;
}
}catch (const fs::filesystem_error& e){
std::cerr << "EXECUTAR ESSA AÇÃO: " << e.what() << '\n';
return false;
}
return true;
}
E por final, nosso arquivo: vim main.cpp
:
Note que passamos o
--debug
como parâmetro de forma opcional!
#include "limpeza.hpp"
int main(int argc, char** argv){
bool debug {false};
std::string param {};
if(argc > 1){
param = argv[1];
if(param == "--debug"){
debug = true;
}else{
std::cerr << "Use: " << argv[0] << " [--debug]\n";
}
}
auto lp = std::make_unique<Limpeza>(debug);
lp->run();
return 0;
}
limpeza.hpp
#pragma once
#include <iostream>
#include <filesystem>
#include <memory>
#include <array>
#include <fstream>
#include "colors.hpp"
namespace fs = std::filesystem;
class Limpeza {
bool m_debug;
std::string m_path;
const std::string m_home = std::getenv("HOME");
const fs::path m_trash = ".local/share/Trash/files";
const fs::path m_thumbs = ".cache/thumbnails";
const fs::path m_chrome = ".cache/google-chrome";
const fs::path m_fox = ".cache/mozilla";
const fs::path m_opera = ".cache/opera";
const fs::path m_recent = ".local/share/recently-used.xbel";
const fs::path m_bash_h = ".bash_history";
protected:
void print(bool, const std::string&);
bool clean_dir(const std::array<std::string, 3>& = {});
bool clean_file(const std::array<std::string, 3>& = {});
public:
Limpeza(bool);
void run();
};
limpeza.cpp
#include "limpeza.hpp"
Limpeza::Limpeza(bool debug) : m_debug(debug){
m_path = {};
}
void Limpeza::run(){
this->clean_dir({m_trash, "Lixeira esvaziada", "esvaziar lixeira"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_file({m_recent, "Arquivos recentes limpados", "limpar arquivos recentes"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_file({m_bash_h, "Histórico do Bash removido", "limpar histórico do Bash"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_thumbs, "Thumbnails removidas", "remover cache das Thumbnails"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_chrome, "Cache do Chrome limpado", "remover cache do Chrome"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_fox, "Cache do Firefox limpado", "remover cache do Firefox"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
this->clean_dir({m_opera, "Cache do Opera limpado", "remover cache do Opera"});
if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
}
void Limpeza::print(bool action, const std::string& msg){
(action) ?
std::cout << hey::green + "\u2705 " + msg + " com sucesso!" + hey::off << '\n' :
std::cerr << hey::red + "\u274C Falha ao " + msg + "." + hey::off << '\n';
}
bool Limpeza::clean_dir(const std::array<std::string, 3>& arr){
m_path = m_home + '/' + arr[0];
if(m_debug){
this->print(true, arr[1]);
return true;
}
try {
if (fs::exists(m_path) && fs::is_directory(m_path)) {
if (fs::is_empty(m_path)) {
return false;
}else{
if(fs::remove_all(m_path)){
this->print(true, arr[1]);
fs::create_directory(m_path);
}else{
this->print(false, arr[2]);
return false;
}
}
}else{
return false;
}
}catch (const fs::filesystem_error& e){
std::cerr << "EXECUTAR ESSA AÇÃO: " << e.what() << '\n';
return false;
}
return true;
}
bool Limpeza::clean_file(const std::array<std::string, 3>& arr){
m_path = m_home + '/' + arr[0];
if(m_debug){
this->print(true, arr[1]);
return true;
}
try {
if (fs::exists(m_path) && fs::is_regular_file(m_path)) {
std::size_t size = std::filesystem::file_size(m_path);
if(size == 0){
return false;
}
if(fs::remove_all(m_path)){
this->print(true, arr[1]);
std::ofstream out(m_path);
out.close();
}else{
this->print(false, arr[2]);
return false;
}
}else{
return false;
}
}catch (const fs::filesystem_error& e){
std::cerr << "EXECUTAR ESSA AÇÃO: " << e.what() << '\n';
return false;
}
return true;
}
main.cpp
#include "limpeza.hpp"
int main(int argc, char** argv){
bool debug {false};
std::string param {};
if(argc > 1){
param = argv[1];
if(param == "--debug"){
debug = true;
}else{
std::cerr << "Use: " << argv[0] << " [--debug]\n";
}
}
auto lp = std::make_unique<Limpeza>(debug);
lp->run();
return 0;
}
colors.hpp
que é da postagem que falei.Para compilar vamos usar esse CMakeLists.txt
:
cmake_minimum_required(VERSION 3.10)
set_property(GLOBAL PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
project(Limpeza
LANGUAGES CXX
VERSION 0.0.1
)
add_compile_options(-g -Wall -Wextra -Wpedantic -pedantic)
if(CHECK_MEM)
message("Compilando com libasan. Saiba mais: <https://github.com/google/sanitizers/>")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()
set (CMAKE_CXX_STANDARD 23)
add_executable(limpeza main.cpp limpeza.cpp)
Note no CMakeLists.txt
que podemos passar o parâmetro opcional: -DCHECK_MEM=ON
para o usar a libasan
que é a biblioteca: sanitizers do Google, para mais informações veja o vídeo: 10 Dicas de Flags e Parâmetros para GNU GCC.
Se quiser fazer o download de todos aquivos clique no botão abaixo para fazer o download do cleanup.zip
Logo você pode usar o CMake assim(com libasan):
cmake -B build . -DCHECK_MEM=ON
Ou somente:
cmake -B build .
Em seguida, compilar e testar com modo debug
:
cmake --build build
build/limpeza --debug
Se quiser instalar e usar para fazer suas limpezas, recomendo usar localmente(só para seu usuário):
mkdir -p ~/.local/bin
echo 'export PATH="${PATH}:${HOME}/.local/bin" >> ~/.bashrc'
exec $SHELL
install -v ./build/limpeza ~/.local/bin
Teste de novo para ver tudo que será ou não removido e depois rode definitivamente:
Lembrando que sem ser no modo debug, somente as ações que ele executar haverá saída. Po exemplo, você não tem o Opera instalado, ou já rodou o comando antes, ele não fará nada e nem exibirá!
limpeza
Saída:
Se quiser que o histórico do terminal fique limpo após rodar tudo, adicione o isso ao final do seu ~/.bashrc
assim:
limpeza(){
${HOME}/.local/bin/limpeza $@
history -c
}
E releia o ~/.bashrc
:
exec $SHELL
# Ou
source ~/.bashrc
Faça assim, pois rodar processos como esse com
std::system
além de não funcionar não é recomendado!
Futuramente pretendo mostrar outros scripts que eu fiz com C++ e depois organizar todos e pôr em um único repositório no GitHub.
Espero que tenham gostado dessa mini-aventura! 😎
cpp bash shellscript comandos cppdaily