Created
September 13, 2023 08:54
-
-
Save code-yeongyu/6e770ff679790157ff83e5880a2d4b8f to your computer and use it in GitHub Desktop.
Safely delete with recovery in a sleek CLI interface. Fully compatible with `rm`. Based on `rip`.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import shutil | |
import subprocess | |
try: | |
from rich.console import Console | |
from rich.style import Style | |
except ImportError: | |
import pip | |
pip.main(["install", "rich"]) | |
from rich.console import Console | |
from rich.style import Style | |
try: | |
import typer | |
except ImportError: | |
import pip | |
pip.main(["install", "typer"]) | |
import typer | |
app = typer.Typer() | |
def is_unix_like() -> bool: | |
"""Check if the current operating system is Unix-like.""" | |
return os.name == "posix" | |
def is_rip_available() -> bool: | |
"""Check if rip is available in the system's PATH.""" | |
return shutil.which("rip") is not None | |
def rm_remove( | |
filenames: list[str], | |
overwrite: bool, | |
undelete: bool, | |
same_fs: bool, | |
directory: bool, | |
verbose: bool, | |
interactive: bool, | |
force: bool, | |
recursive: bool, | |
): | |
Console().print( | |
"Warning: Your Command calls 'rm'. Deleted files can't be recovered.", | |
style=Style(color="YELLOW"), | |
) | |
cmd = ["/bin/rm"] | |
if overwrite: | |
cmd.append("-P") | |
if undelete: | |
cmd.append("-W") | |
if same_fs: | |
cmd.append("-x") | |
if directory: | |
cmd.append("-d") | |
if verbose: | |
cmd.append("-v") | |
if interactive: | |
cmd.append("-i") | |
if force: | |
cmd.append("-f") | |
if recursive: | |
cmd.append("-r") | |
cmd.extend(filenames) | |
subprocess.run(cmd, check=True) | |
def rip_remove( | |
filenames: list[str], | |
verbose: bool, | |
force: bool, | |
interactive: bool, | |
): | |
"""Remove files using rip, | |
but support with verbose, force, and interactive options.""" | |
cmd = ["rip"] | |
if interactive: | |
for filename in filenames: | |
if not os.path.exists(filename): | |
if force: | |
continue | |
Console(stderr=True).print( | |
f"Error: [bold]{filename}[/bold] does not exist.", | |
style=Style(color="RED"), | |
) | |
raise typer.Exit(code=1) | |
if interactive: | |
typer.confirm( | |
f"Are you sure you want to delete '{typer.style(filename, bold=True)}'?", | |
abort=True, | |
) | |
subprocess.run(cmd + [filename], check=True) | |
if verbose: | |
typer.echo(f"Removed {filename}") | |
else: | |
# check every filenames are exist | |
filenames_to_remove = [] | |
for filename in filenames: | |
if not os.path.exists(filename) and not force: | |
Console(stderr=True).print( | |
f"Error: [bold]{filename}[/bold] does not exist.", | |
style=Style(color="RED"), | |
) | |
raise typer.Exit(code=1) | |
filenames_to_remove.append(filename) | |
cmd.extend(filenames) | |
subprocess.run(cmd, check=True) | |
if verbose: | |
for filename in filenames_to_remove: | |
typer.echo(f"Removed {filename}") | |
console = Console() | |
console.print("Done! ✨", style=Style(bold=True)) | |
console.print( | |
"Tip: You can use 'rip -u' command to recover deleted files.", style=Style(color="YELLOW", italic=True) | |
) | |
@app.command() | |
def modern_rm( | |
filenames: list[str] = typer.Argument( | |
..., | |
help="Names of the files or directories to remove", | |
), | |
force: bool = typer.Option( | |
False, | |
"-f", | |
"--force", | |
help="Ignore nonexistent files and arguments, never prompt", | |
), | |
interactive: bool = typer.Option( | |
False, | |
"-i", | |
"--interactive", | |
help="Prompt before every removal", | |
), | |
once: bool = typer.Option( | |
False, | |
"-I", | |
"--once", | |
help=f"Prompt once before removing more than three files, or when removing recursively {typer.style('[calls original rm]', fg=typer.colors.RED)}", | |
), | |
directory: bool = typer.Option( | |
False, | |
"-d", | |
"--directory", | |
help=f"Remove directories {typer.style('[calls original rm]', fg=typer.colors.RED)}", | |
), | |
recursive: bool = typer.Option( | |
False, | |
"-r", | |
"--recursive", | |
help="Remove directories and their contents recursively", | |
), | |
overwrite: bool = typer.Option( | |
False, | |
"-P", | |
help=f"Overwrite regular files before deleting them {typer.style('[calls original rm]', fg=typer.colors.RED)}", | |
), | |
undelete: bool = typer.Option( | |
False, | |
"-W", | |
help=f"Attempt to undelete the named files {typer.style('[calls original rm]', fg=typer.colors.RED)}", | |
), | |
same_fs: bool = typer.Option( | |
False, | |
"-x", | |
help=f"Stay on the same filesystem {typer.style('[calls original rm]', fg=typer.colors.RED)}", | |
), | |
verbose: bool = typer.Option( | |
False, | |
"-v", | |
"--verbose", | |
help="Display detailed information about the deletion process.", | |
), | |
): | |
"""Safely delete with recovery in a sleek CLI interface. Fully compatible with 'rm'. Based on 'rip'. | |
Written by YeonGyu Kim ([email protected])""" | |
if not is_unix_like(): | |
console = Console(stderr=True) | |
console.print( | |
"Error: This command is only available on Unix-like systems.", | |
style=Style(color="RED"), | |
) | |
raise typer.Exit(code=1) | |
if not is_rip_available(): | |
console = Console(stderr=True) | |
console.print( | |
"Error: 'rip' command is not available. Please install it and try again.", | |
style=Style(color="RED"), | |
) | |
console.print( | |
"You can install it at 'https://github.com/nivekuil/rip'.", | |
style=Style(color="YELLOW"), | |
) | |
raise typer.Exit(code=1) | |
if once and len(filenames) > 3: | |
typer.confirm("Are you sure you want to delete these files?", abort=True) | |
if force: | |
interactive = False | |
is_rm_required = any((overwrite, undelete, same_fs, directory)) | |
if is_rm_required: | |
rm_remove( | |
filenames=filenames, | |
overwrite=overwrite, | |
undelete=undelete, | |
same_fs=same_fs, | |
directory=directory, | |
verbose=verbose, | |
interactive=interactive, | |
force=force, | |
recursive=recursive, | |
) | |
return | |
rip_remove( | |
filenames=filenames, | |
verbose=verbose, | |
force=force, | |
interactive=interactive, | |
) | |
if __name__ == "__main__": | |
app() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment