Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be9867a007 | ||
| e76c8726a2 |
22
README.md
22
README.md
@@ -7,11 +7,23 @@ It moves files to a per-user _trash_ instead of permanently deleting them, while
|
||||
|
||||
## Features
|
||||
|
||||
- Move files and directories to a **trash folder** instead of permanent deletion
|
||||
- Restore deleted files by **short ID or exact basename**
|
||||
- Empty trash safely
|
||||
- Supports `-r`, `-f`, `-i`, `--skip-trash` options
|
||||
- Works with `sudo` for root-owned files
|
||||
- Move files and directories to a **Trash folder** instead of permanent deletion
|
||||
- Restore deleted files by **short ID or exact basename**
|
||||
- Empty trash safely
|
||||
- Supports `-r`, `-f`, `-i`, `--skip-trash` options
|
||||
- Works with `sudo` for root-owned files
|
||||
- Automatically prunes Trash entries older than `$RESRM_TRASH_LIFE` days (default **7**, minimum **1**)
|
||||
> Note: if you need immediate deletion, use the regular `rm` command instead.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
To control how long trashed files are kept, add this line to your shell configuration (e.g. `~/.bashrc`):
|
||||
|
||||
```bash
|
||||
export RESRM_TRASH_LIFE=10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "resrm"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
description = "drop-in replacement for rm with undo/restore built-in."
|
||||
authors = ["Marco D'Aleo <marco@marcodaleo.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
@@ -53,6 +53,42 @@ def get_trash_paths() -> tuple[Path, Path]:
|
||||
TRASH_DIR, META_FILE = get_trash_paths()
|
||||
DATEFMT = "%Y-%m-%d %H:%M"
|
||||
|
||||
def prune_old_trash():
|
||||
"""Remove trash entries older than RESRM_TRASH_LIFE days (default 7)."""
|
||||
try:
|
||||
life_days = int(os.environ.get("RESRM_TRASH_LIFE", "7"))
|
||||
except ValueError:
|
||||
life_days = 7
|
||||
|
||||
if life_days < 1:
|
||||
life_days = 1
|
||||
|
||||
cutoff = datetime.datetime.now() - datetime.timedelta(days=life_days)
|
||||
removed = 0
|
||||
|
||||
for entry in list(meta): # make copy since we'll modify meta
|
||||
try:
|
||||
ts = datetime.datetime.fromisoformat(entry["timestamp"])
|
||||
except Exception:
|
||||
continue # skip malformed entries
|
||||
|
||||
if ts < cutoff:
|
||||
f = TRASH_DIR / entry["id"]
|
||||
try:
|
||||
if f.exists():
|
||||
if f.is_dir():
|
||||
shutil.rmtree(f, ignore_errors=True)
|
||||
else:
|
||||
f.unlink(missing_ok=True)
|
||||
meta.remove(entry)
|
||||
removed += 1
|
||||
except Exception as e:
|
||||
print(f"Failed to prune {f}: {e}")
|
||||
|
||||
if removed > 0:
|
||||
save_meta(meta)
|
||||
print(f"Pruned {removed} trash entr{'y' if removed == 1 else 'ies'} older than {life_days} da{'y' if life_days == 1 else 'ys'}.")
|
||||
|
||||
def load_meta() -> List[Dict]:
|
||||
if META_FILE.exists():
|
||||
try:
|
||||
@@ -304,6 +340,7 @@ def move_to_trash(path: Path, interactive: bool, force: bool, skip_trash: bool):
|
||||
def main(argv: Optional[List[str]] = None):
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
prune_old_trash()
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument("paths", nargs="*", help="files to remove")
|
||||
parser.add_argument("-r", action="store_true", help="recursive")
|
||||
|
||||
Reference in New Issue
Block a user