DELTREE command in Python


DELTREE command in Python

One of the most useful commands when i used DOS, was deltree. Quickly and easy, deleting large areas of the hard disk, with no point of return... In Linux, there isn't such a command, or perhaps, it's a bit disguised, but never the less, it hasn't the features of the original deltree command or the one, we have here.

The script it self is nothing much. Make sure to have installed the tqdm module, as it's used for the progress bar display.

You have the following options to use:

usage: deltree [-h] [-s] [-y] [-c] [-q] directory

Delete a directory and all its contents. Use -h or --help for help.

positional arguments:
  directory       The path of the directory to delete

options:
  -h, --help      show this help message and exit
  -s, --simulate  Simulate deletion by listing files instead of deleting them
  -y, --yes       Disable confirmation prompt and delete without asking
  -q, --quiet     Suppress information output

As you see, there is also a simulate option, which will not delete any files, but it will show you a list of the directories/files, that are going to be deleted and a total summary of size and number of dirs/files, that would be deleted. The program will ask you once for confirmation and then start the process of deletion, showing a simple "linux-like" progress bar.

The program, may be simple but for sure can be developed more and add more features. For example one could implement an except switch to not delete certain files/dirs or add a protection of removing file while having sudo privileges or protection from removing files/dir, from the root directory etc.

Just copy/paste and make it executable:

#!/usr/bin/python3
import os
import argparse
from tqdm import tqdm

VERSION="1.0.0"
NAME="Deltree"
QUIET=False

def qprint(s,quiet=False):
    global QUIET
    if QUIET: return
    print(s)

def format_size(size_bytes):
    """Convert a size in bytes to a more readable format (KB, MB, GB)."""
    if size_bytes < 1024:
        return f"{size_bytes} bytes"
    elif size_bytes < 1024**2:
        return f"{size_bytes / 1024:.2f} KB"
    elif size_bytes < 1024**3:
        return f"{size_bytes / 1024**2:.2f} MB"
    else:
        return f"{size_bytes / 1024**3:.2f} GB"

def delete_directory(dir_path, simulate=False, force=False):
    """Delete a directory and all its contents or list them if simulate is True."""
    if os.path.exists(dir_path):
        if simulate:
            qprint(f'Simulating deletion of directory: {dir_path}')
            total_files = 0
            total_dirs = 0
            total_size = 0

            for root, dirs, files in os.walk(dir_path):
                level = root.replace(dir_path, '').count(os.sep)
                indent = ' ' * 4 * (level)
                print(f'{indent}{os.path.basename(root)}/')
                subindent = ' ' * 4 * (level + 1)
                for f in files:
                    file_path = os.path.join(root, f)
                    file_size = os.path.getsize(file_path)
                    total_size += file_size
                    total_files += 1
                    print(f'{subindent}{f}')
                total_dirs += len(dirs)

            # Display totals
            qprint(f'\n    Total directories: {total_dirs:,.0f}')
            qprint(f'    Total files: {total_files:,.0f}')
            qprint(f'    Total size of files: {format_size(total_size)} / {total_size} bytes\n')
        else:
            if not force:
                confirmation = input(f'Delete directory "{dir_path}" and all its contents? (yes/no): ')
                if confirmation.lower() == 'yes' or confirmation.lower() == 'y':
                    print("")
                else:
                    qprint('Deletion canceled.')
                    return

            # Count total files and directories for progress bar
            total_items = sum(len(files) + len(dirs) for _, dirs, files in os.walk(dir_path))
            total_files = 0
            total_dirs = 0
            total_size = 0

            with tqdm(total=total_items, desc='Deleting items', unit='item') as pbar:
                for root, dirs, files in os.walk(dir_path, topdown=False):
                    for name in files:
                        file_path = os.path.join(root, name)
                        total_size += os.path.getsize(file_path)  # Accumulate file size
                        total_files += 1  # Count files
                        os.remove(file_path)
                        pbar.update(1)
                    for name in dirs:
                        total_dirs += 1  # Count directories
                        os.rmdir(os.path.join(root, name))
                        pbar.update(1)
            os.rmdir(dir_path)  # Finally remove the root directory
            qprint(f'\nDirectory {dir_path} and all its contents have been deleted.')
            qprint(f'\n    Total files: {total_files}')
            qprint(f'    Total directories: {total_dirs}')
            qprint(f'    Total size of files: {format_size(total_size)} / {total_size} bytes\n')
    else:
        qprint(f'Directory {dir_path} does not exist.')

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description='Delete a directory and all its contents. Use -h or --help for help.')
    parser.add_argument('directory', type=str, help='The path of the directory to delete')
    parser.add_argument('-s', '--simulate', action='store_true', help='Simulate deletion by listing files instead of deleting them')
    parser.add_argument('-y', '--yes', action='store_true', help='Disable confirmation prompt and delete without asking')
    parser.add_argument('-q', '--quiet', action='store_true', help='Suppress information output')

    args = parser.parse_args()
    QUIET = args.quiet
    qprint(f"{NAME} v{VERSION}")
    delete_directory(args.directory, simulate=args.simulate, force=args.yes)

Add a comment

Previous Next