Using C++ as Shell Script

🗞️ It sounds crazy, but in addition to being great exercise, it greatly improves task performance.


Using C++ as Shell Script


A while ago I had created a script in Bash(Shell Script) which cleaned up some things on my system, including:

  • The cache of images that were viewed, including thumbnails;
  • The cache of the browsers I had installed on my machine: Google Chrome, Firefox and Opera;
  • History with Bash;
  • Recent files;
  • And the trash can!

The script worked normally, I just had to run the limpeza command in terminal.

However, one fine day when I ran this script, I noticed that it took a long time to execute. The reason for this was that in the trash there were several .iso files, together I think they were about 5GB in size!

And then I thought asking myself:

— Why did Bash take so long to remove???

And I think:

— If I wrote this script in C++, would it take so long?!

So, I decided to do it and test it! After finishing writing, I deleted about 10GB of .iso (I threw it in the trash) and ran the post-compiled code to test with the command time and compared the result of the same command with that of the Bash script.

And the difference was BIG! In other words, the same script made in C++ was much faster!

Well, it’s been a long time, but I decided to post it here, because this “script” serves as an exercise for those who are training their C++ codes, it’s a good idea to use: C++ as Shell Script to improve your skills!


Writing the code

As we are going to compile with CMake, the ideal is to create a folder for the project:

mkdir cleanup
cd cleanup

All files

cleanup.hpp

#pragma once
#include <iostream>
#include <filesystem>
#include <memory>
#include <array>
#include <fstream>
#include "colors.hpp"

namespace fs = std::filesystem;

class Cleanup {

    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:
        Cleanup(bool);
        void run();
};

colors.cpp

#ifndef COLORS_H
#define COLORS_H
#include <iostream>

namespace hey {

  const std::string gray = "\033[30;10m", // normal
        grayn = "\033[30;1m", // bold
        grayf = "\033[30;2m", // weak
        grayi = "\033[30;3m", // italics
        grays = "\033[30;4m", // underline
        grayp = "\033[30;5m", // blinking
        grayb = "\033[30;7m", // background
        grayc = "\033[30;9m", // canceled

        red = "\033[31;10m", // normal
        redn = "\033[31;1m", // bold
        redf = "\033[31;2m", // weak
        redi = "\033[31;3m", // italics
        reds = "\033[31;4m", // underscore
        redp = "\033[31;5m", // flashing
        redb = "\033[31;7m", // background
        redc = "\033[31;9m", // canceled

        green = "\033[32;10m", // normal
        greenn = "\033[32;1m", // bold
        greenf = "\033[32;2m", // weak
        greeni = "\033[32;3m", // italics
        greens = "\033[32;4m", // underline
        greenp = "\033[32;5m", // blinking
        greenb = "\033[32;7m", // background
        greenc = "\033[32;9m", // canceled

        yellow = "\033[33;10m", // normal
        yellown = "\033[33;1m", // bold
        yellowf = "\033[33;2m", // weak
        yellowi = "\033[33;3m", // italics
        yellows = "\033[33;4m", // underlined
        yellowp = "\033[33;5m", // blinking
        yellowb = "\033[33;7m", // background
        yellowc = "\033[33;9m", // canceled

        blue = "\033[34;10m", // normal
        bluen = "\033[34;1m", // bold
        bluef = "\033[34;2m", // weak
        bluei = "\033[34;3m", // italics
        blues = "\033[34;4m", // underline
        bluep = "\033[34;5m", // blinking
        blueb = "\033[34;7m", // background
        bluec = "\033[34;9m", // canceled

        purple = "\033[35;10m", // normal
        purplen = "\033[35;1m", // bold
        purplef = "\033[35;2m", // weak
        purplei = "\033[35;3m", // italics
        purples = "\033[35;4m", // underline
        purplep = "\033[35;5m", // blinking
        purpleb = "\033[35;7m", // background
        purplec = "\033[35;9m", // canceled

        cyan = "\033[36;10m", // normal
        cyann = "\033[36;1m", // bold
        cyanf = "\033[36;2m", // weak
        cyani = "\033[36;3m", // italics
        cyans = "\033[36;4m", // underscore
        cyanp = "\033[36;5m", // blinking
        cyanb = "\033[36;7m", // background
        cyanc = "\033[36;9m", // canceled

        white = "\033[38;10m", // normal
        whiten = "\033[38;1m", // bold
        whitef = "\033[38;2m", // weak
        whitei = "\033[38;3m", // italics
        whites = "\033[38;4m", // underscore
        whitep = "\033[38;5m", // blinking
        whiteb = "\033[38;7m", // background
        whitec = "\033[38;9m", // canceled

        off = "\033[m"; // turns off
}

#endif

cleanup.cpp

#include "cleanup.hpp"

Cleanup::Cleanup(bool debug) : m_debug(debug){
  m_path = {};
}

void Cleanup::run(){
  this->clean_dir({m_trash, "Recycle bin emptied", "empty trash"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}

  this->clean_file({m_recent, "Recent files cleaned", "clean recent files"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}

  this->clean_file({m_bash_h, "Bash history removed", "clear Bash history"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}

  this->clean_dir({m_thumbs, "Thumbnails removed", "remove Thumbnails cache"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}

  this->clean_dir({m_chrome, "Chrome cache cleaned", "remove Chrome cache"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}

  this->clean_dir({m_fox, "Firefox cache cleared", "remove Firefox cache"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}

  this->clean_dir({m_opera, "Opera cache cleaned", "remove Opera cache"});
  if(m_debug){ std::cout << hey::yellow << m_path << hey::off << "\n\n";}
}

void Cleanup::print(bool action, const std::string& msg){
  (action) ?
    std::cout << hey::green + "\u2705 " + msg + "!" + hey::off << '\n' :
    std::cerr << hey::red + "\u274C Failed to " + msg + "." + hey::off << '\n';
}

bool Cleanup::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 << "PERFORM THIS ACTION: " << e.what() << '\n';
    return false;
  }
  return true;
}

bool Cleanup::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 << "PERFORM THIS ACTION: " << e.what() << '\n';
    return false;
  }
  return true;
}

main.cpp

#include "cleanup.hpp"

int main(int argc, char** argv){
    bool debug {false};
    if(argc > 1){
        std::string param = argv[1];
        if(param == "--debug"){
            debug = true;
        }else{
            std::cerr << "Use: " << argv[0] << " [--debug]\n";
            return EXIT_FAILURE;
        }
    }
    auto lp = std::make_unique<Cleanup>(debug);
    lp->run();
    return EXIT_SUCCESS;
}

Compiling with CMake, running and installing

To compile we will use this CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
set_property(GLOBAL PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)

project(Cleanup
 LANGUAGES CXX
 VERSION 0.0.1
)

add_compile_options(-g -Wall -Wextra -Wpedantic -pedantic)
if(CHECK_MEM)
 message("Compiling with libasan. Learn more: <https://github.com/google/sanitizers/>")
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
endif()

set (CMAKE_CXX_STANDARD 23)
add_executable(cleanup main.cpp cleanup.cpp)

Note in CMakeLists.txt that we can pass the optional parameter: -DCHECK_MEM=ON to use libasan which is the library: Google sanitizers, for more information see the video: 10 Flags and Parameters Tips for GNU GCC.

If you want to download all files click on the button below to download cleanup.zip:

Download cleanup.zip

So you can use CMake like this (with libasan):

cmake -B build . -DCHECK_MEM=ON

Or just: cmake -B build .

Then compile and test with debug mode:

cmake --build build
build/clean --debug

If you want to install and use it to do your cleanup, I recommend using it locally (only for your user):

mkdir -p ~/.local/bin
echo 'export PATH="${PATH}:${HOME}/.local/bin" >> ~/.bashrc'
exec $SHELL
install -v ./build/cleanup ~/.local/bin

Test again to see everything that will or will not be removed and then run it definitively:

Remembering that without being in debug mode, only the actions it executes will be output. For example, you don’t have Opera installed, or you’ve already run the command before, it won’t do anything or display it!

cleanup

Output:

Output cleaning command done with C++

If you want the terminal history to be clean after running everything, add an alias like this:

vim ~/.bashrc and paste this code at the end of the file:

cleanup(){
  ${HOME}/.local/bin/cleanup $@
  history -c
}

And run: exec $SHELL or source ~/.bashrc

Do it this way, as running processes like this with std::system not only doesn’t work, it’s not recommended!


In the future I intend to show other scripts that I made with C++ and then organize them all and put them in a single repository on GitHub.

I hope you enjoyed this mini-adventure! 😎


cpp bash shellscript commands cppdaily


Share


YouTube channel

Subscribe


Marcos Oliveira

Marcos Oliveira

Software developer
https://github.com/terroo