I first started with Python and that worked well. I then did a perl version. That went well. So, I thought – why not go for the jugular – write a c++ one. I call it file_audit – save a csv formatted file so I can review and sort by date to see what I need to save or delete. This code requires “.pdf” not “pdf” for the extension.
Here is my prompt
write a c++ program that take argv of --dir for directory, --csvfile for output csv file and type for extension file type such as .pdf or txt. write the output to a the specified csv file, the csv file should contane file name, direcotry, size and date in format YYYY/MM/DD and time
Here is what it generated
// Include the necessary libraries
#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <iomanip>
// Use the std namespace
using namespace std;
// Define a function to parse the command line arguments and return the values
void parse_args(int argc, char* argv[], string& dir, string& csvfile, string& type) {
// Loop through the arguments
for (int i = 1; i < argc; i++) {
// Check if the argument is --dir
if (string(argv[i]) == "--dir") {
// Check if the next argument exists and is not another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
// Assign the next argument to dir
dir = argv[i + 1];
// Increment i to skip the next argument
i++;
}
else {
// Throw an exception if the argument is invalid
throw invalid_argument("Missing or invalid value for --dir");
}
}
// Check if the argument is --csvfile
else if (string(argv[i]) == "--csvfile") {
// Check if the next argument exists and is not another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
// Assign the next argument to csvfile
csvfile = argv[i + 1];
// Increment i to skip the next argument
i++;
}
else {
// Throw an exception if the argument is invalid
throw invalid_argument("Missing or invalid value for --csvfile");
}
}
// Check if the argument is --type
else if (string(argv[i]) == "--type") {
// Check if the next argument exists and is not another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
// Assign the next argument to type
type = argv[i + 1];
// Increment i to skip the next argument
i++;
}
else {
// Throw an exception if the argument is invalid
throw invalid_argument("Missing or invalid value for --type");
}
}
else {
// Throw an exception if the argument is unknown
throw invalid_argument("Unknown option: " + string(argv[i]));
}
}
}
// Define a function to format a file time point as YYYY/MM/DD HH:MM:SS
string format_time(filesystem::file_time_type file_time) {
// Convert the file time point to system clock time point
auto sys_time = chrono::time_point_cast<chrono::system_clock::duration>(file_time - filesystem::file_time_type::clock::now() + chrono::system_clock::now());
// Convert the system clock time point to time_t
time_t tt = chrono::system_clock::to_time_t(sys_time);
// Convert the time_t to tm structure
tm local_tm = *localtime(&tt);
// Create a string stream to format the output
stringstream ss;
// Write the year, month, day, hour, minute and second to the stream with leading zeros and separators
ss << setw(4) << setfill('0') << local_tm.tm_year + 1900 << "/";
ss << setw(2) << setfill('0') << local_tm.tm_mon + 1 << "/";
ss << setw(2) << setfill('0') << local_tm.tm_mday << " ";
ss << setw(2) << setfill('0') << local_tm.tm_hour << ":";
ss << setw(2) << setfill('0') << local_tm.tm_min << ":";
ss << setw(2) << setfill('0') << local_tm.tm_sec;
// Return the formatted string
return ss.str();
}
// Define a function to write the output to a csv file
void write_csv(const string& dir, const string& csvfile, const string& type) {
// Create an output file stream and open the csv file
ofstream ofs;
ofs.open(csvfile);
// Check if the file stream is open and ready
if (ofs.is_open()) {
// Write the header row to the csv file
ofs << "File Name,Directory,Size,Date\n";
// Loop through the directory entries in the given directory recursively
for (const auto& entry : filesystem::recursive_directory_iterator(dir)) {
// Check if the entry is a regular file and has the given extension type
if (entry.is_regular_file() && entry.path().extension() == type) {
// Write the file name, directory, size and date to the csv file with commas as separators and quotes as delimiters
ofs << "\"" << entry.path().filename().string() << "\",";
ofs << "\"" << entry.path().parent_path().string() << "\",";
ofs << "\"" << entry.file_size() << "\",";
ofs << "\"" << format_time(entry.last_write_time()) << "\"\n";
}
}
// Close the file stream
ofs.close();
}
else {
// Throw an exception if the file stream is not open or ready
throw runtime_error("Failed to open or write to " + csvfile);
}
}
// Define the main function
int main(int argc, char* argv[]) {
try {
// Declare variables for directory, csv file and type arguments
string dir, csvfile, type;
// Parse the command line arguments and assign values to variables
parse_args(argc, argv, dir, csvfile, type);
// Write the output to a csv file with given arguments
write_csv(dir, csvfile, type);
// Print a success message to standard output
cout << "Output written to " << csvfile << "\n";
}
catch (const exception& e) {
// Print an error message to standard error with exception message
cerr << "Error: " << e.what() << "\n";
return 1;
}
}
cool and it compiles.
I asked it to add a “-h” for help and got this. I ended up just pasting in the changed section.
// Define a function to parse the command line arguments and return the values
void parse_args(int argc, char* argv[], string& dir, string& csvfile, string& type) {
// Loop through the arguments
for (int i = 1; i < argc; i++) {
// Check if the argument is -h
if (string(argv[i]) == "-h") {
// Print the usage message and exit
cout << "Usage: " << argv[0] << " --dir <directory> --csvfile <csv file> --type <file type>\n";
cout << "Options:\n";
cout << " --dir <directory> Specify the directory to scan recursively\n";
cout << " --csvfile <csv file> Specify the output csv file name\n";
cout << " --type <file type> Specify the file extension type to filter\n";
cout << " -h Show this help message and exit\n";
exit(0);
}
// Check if the argument is --dir
else if (string(argv[i]) == "--dir") {
// Check if the next argument exists and is not another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
// Assign the next argument to dir
dir = argv[i + 1];
// Increment i to skip the next argument
i++;
}
else {
// Throw an exception if the argument is invalid
throw invalid_argument("Missing or invalid value for --dir");
}
}
// Check if the argument is --csvfile
else if (string(argv[i]) == "--csvfile") {
// Check if the next argument exists and is not another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
// Assign the next argument to csvfile
csvfile = argv[i + 1];
// Increment i to skip the next argument
i++;
}
else {
// Throw an exception if the argument is invalid
throw invalid_argument("Missing or invalid value for --csvfile");
}
}
// Check if the argument is --type
else if (string(argv[i]) == "--type") {
// Check if the next argument exists and is not another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
// Assign the next argument to type
type = argv[i + 1];
// Increment i to skip the next argument
i++;
}
else {
// Throw an exception if the argument is invalid
throw invalid_argument("Missing or invalid value for --type");
}
}
else {
// Throw an exception if the argument is unknown
throw invalid_argument("Unknown option: " + string(argv[i]));
}
}
}
For FreeBSD 12.4 – I had to use gcc12 to compile it.
for LLVM – c++
c++ -std=gnu++2a file_audit.cpp -o file_audit_2
Here is a typical run
./file_audit –dir $HOME –type .py –csvfile test2.csv
[jon2allen@freebsd12_4 ~/c++]$ head test2.csv File Name,Directory,Size,Date "daily_weather_obs_chart.py","/home/jon2allen/github/weather_obs","11008","2023/01/24 10:43:09" "date_from_obs.py","/home/jon2allen/github/weather_obs","1540","2023/01/24 10:43:09" "email_obs_err.py","/home/jon2allen/github/weather_obs","1078","2023/01/24 10:43:09" "get_noaa_sites.py","/home/jon2allen/github/weather_obs","5233","2023/01/24 10:43:09" "get_station_latest.py","/home/jon2allen/github/weather_obs","2234","2023/01/24 10:43:09" "lambda_handler.py","/home/jon2allen/github/weather_obs","718","2023/01/24 10:43:09" "noaa_duplicates.py","/home/jon2allen/github/weather_obs","1655","2023/01/24 10:43:09" "noaa_tidal_potomac.py","/home/jon2allen/github/weather_obs","6490","2023/01/24 10:43:09" "noaa_tide_chart_puller.py","/home/jon2allen/github/weather_obs","12372","2023/01/24 10:43:09"
