From 96204981706f7e7339a19c437bc2a40738405650 Mon Sep 17 00:00:00 2001 From: jpmvaz Date: Sun, 13 Sep 2026 19:55:41 +0100 Subject: [PATCH] v1 --- README.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ TreeAndDir.py | 78 +++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 README.md create mode 100644 TreeAndDir.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..09f8c24 --- /dev/null +++ b/README.md @@ -0,0 +1,206 @@ +# TreeAndDir + +A lightweight Python utility for Windows that captures a complete directory listing and file-tree snapshot of the current working directory and saves them to plain-text files. + +--- + +## Table of Contents + +- [Overview](#overview) +- [Features](#features) +- [Requirements](#requirements) +- [Installation](#installation) +- [Usage](#usage) +- [Output Files](#output-files) +- [Code Changes & Improvements](#code-changes--improvements) +- [Troubleshooting](#troubleshooting) +- [License](#license) + +--- + +## Overview + +`TreeAndDir.py` automates two common Windows diagnostic tasks: + +1. **`dir /s`** — Recursively lists every file and folder in the current directory, including file sizes, dates, and totals. +2. **`tree /F /A`** — Renders the directory structure as an ASCII tree with all file names shown (`/F`) using ASCII characters instead of extended line-drawing characters (`/A`), making the output portable across all editors and terminals. + +Both outputs are written to text files in the same directory where the script is run. + +--- + +## Features + +- **Zero external dependencies** — uses only the Python standard library. +- **Timing & file-size feedback** — reports how long each command took and the size of the output file. +- **Robust error handling** — non-zero exit codes and I/O errors are caught and reported without crashing the script. +- **Smart interactive detection** — the "Press Enter to exit" pause only appears when the script is run interactively; it is suppressed automatically in automated/piped contexts. +- **UTF-8 output with fallback** — files are written as UTF-8; any unencodable characters are replaced rather than raising an exception. +- **Clean exit codes** — exits `0` on full success, `1` if any command produced a warning, making it easy to use in batch/CI pipelines. + +--- + +## Requirements + +| Requirement | Version | +|---|---| +| Operating System | Windows 10 / 11 (requires `cmd.exe`) | +| Python | 3.8 or later | + +No third-party packages are needed. All modules used (`os`, `subprocess`, `sys`, `time`, `pathlib`) are part of the Python standard library. + +--- + +## Installation + +1. **Clone or download** this repository (or just copy `TreeAndDir.py` to any folder). + +2. **Verify Python is installed:** + + ```cmd + python --version + ``` + + If Python is not installed, download it from [python.org](https://www.python.org/downloads/). + +3. No `pip install` step is required. + +--- + +## Usage + +### Interactive (double-click or Command Prompt) + +Navigate to the directory you want to capture, then run: + +```cmd +cd C:\path\to\target\directory +python TreeAndDir.py +``` + +Example session output: + +``` +Running 'dir /s'... + Saved to C:\MyProject\DirOutput.txt (42.3 KB, 0.18s) +Running 'tree /F /A'... + Saved to C:\MyProject\TreeOutput.txt (8.1 KB, 0.05s) + +Done. + +Press Enter to exit... +``` + +### Automated / Scripted + +The script integrates cleanly into batch files or CI pipelines: + +```cmd +cd C:\MyProject +python TreeAndDir.py +IF ERRORLEVEL 1 ( + echo One or more commands produced warnings. +) +``` + +The "Press Enter" prompt is suppressed automatically when stdin is not a terminal (e.g., when output is redirected or the script is called from another process). + +--- + +## Output Files + +Both files are created in the **current working directory** at the time the script is run. + +### `DirOutput.txt` + +Contains the full output of `dir /s`, including: + +- File names, sizes (bytes), and last-modified timestamps for every file. +- Subfolder names and their individual file counts. +- A grand total of files and bytes at the end. + +Example excerpt: + +``` + Volume in drive C is Windows + Volume Serial Number is XXXX-XXXX + + Directory of C:\MyProject + +02/20/2026 10:00 AM . +02/20/2026 10:00 AM .. +02/20/2026 09:55 AM 1,234 TreeAndDir.py +... +``` + +### `TreeOutput.txt` + +Contains the full output of `tree /F /A`, rendering the folder hierarchy as an ASCII diagram. + +Example excerpt: + +``` +C:\MyProject ++---src +| +---main.py +| \---utils.py ++---tests +| \---test_main.py +\---README.md +``` + +> **Note:** The `/A` flag ensures the output uses standard ASCII characters (`+`, `-`, `\`) instead of extended box-drawing characters, making the file readable in all text editors and terminals without encoding issues. + +--- + +## Code Changes & Improvements + +The following improvements were made over the original version: + +### Bug Fixes + +- **Added `/A` flag to `tree`** — the original `tree /F` used extended line-drawing characters which can appear as garbled symbols in some editors. `/A` forces plain ASCII output. + +### Robustness + +- **Error handling** — subprocess errors (`OSError`) and non-zero exit codes are now caught and reported to `stderr` rather than causing an unhandled exception. +- **`check=False` + manual exit-code inspection** — prevents `subprocess.CalledProcessError` from terminating the script unexpectedly; warnings are printed instead. + +### Performance + +- **Direct file streaming** — the output file handle is passed directly to `subprocess.run` via `stdout=fh`, so data flows from `cmd.exe` straight to disk with no intermediate Python string buffering. This is both faster and more memory-efficient for large directory trees. +- **`pathlib.Path`** — replaces `os.path.join` string manipulation with the more readable and Pythonic `Path` API. + +### Usability + +- **Elapsed time and file-size reporting** — each command prints how long it took and how large the output file is, which is useful for diagnosing slow or empty runs. +- **Smart pause suppression** — `sys.stdin.isatty()` check means the `input("Press Enter…")` prompt only appears in interactive sessions, preventing the script from hanging when run from a scheduler or pipeline. +- **Clean exit codes** — `sys.exit(0/1)` enables downstream tooling to detect failures. + +### Code Quality + +- **Type hints** — `run_command` is annotated for clarity and IDE support. +- **Docstrings** — functions are documented with Args/Returns sections. +- **`main()` entry-point pattern** — logic is encapsulated in `main()` and guarded by `if __name__ == "__main__"`, making the module importable without side effects. + +--- + +## Troubleshooting + +**`python` is not recognized** +Ensure Python is installed and added to your `PATH`. Try `py TreeAndDir.py` as an alternative. + +**Output files are empty** +Verify you are running the script from a directory that actually contains files. Run `dir` manually in the same terminal to confirm. + +**`tree` is not available** +`tree.com` is a built-in Windows utility and should always be available. If it is missing (rare in some stripped-down environments), the script will report a non-zero exit code and the output file will contain the error message from `cmd.exe`. + +**Garbled characters in output files** +Both files are written as UTF-8. Open them in an editor configured for UTF-8 (e.g., VS Code, Notepad++). Windows Notepad on older systems may require selecting the encoding manually. + +--- + +## License + +This project is released into the public domain. Use, modify, and distribute freely. diff --git a/TreeAndDir.py b/TreeAndDir.py new file mode 100644 index 0000000..9ef6d81 --- /dev/null +++ b/TreeAndDir.py @@ -0,0 +1,78 @@ +""" +TreeAndDir.py +------------- +Captures a full directory listing (dir /s) and a file tree (tree /F /A) +of the current working directory and saves both outputs to text files. + +Requirements: Python 3.8+, Windows OS (uses cmd.exe built-ins) +""" + +import os +import subprocess +import sys +import time +from pathlib import Path + + +def run_command(args: list[str], output_path: Path, label: str) -> bool: + """ + Execute a shell command and stream its output directly to a file. + + Args: + args: Command + arguments list passed to subprocess. + output_path: Destination file for stdout/stderr. + label: Human-readable name shown in progress messages. + + Returns: + True on success, False if the process exits with a non-zero code. + """ + print(f"Running '{' '.join(args[2:])}'...") + start = time.perf_counter() + + try: + with output_path.open("w", encoding="utf-8", errors="replace") as fh: + result = subprocess.run( + args, + stdout=fh, + stderr=subprocess.STDOUT, + check=False, # handle non-zero exits ourselves + ) + except OSError as exc: + print(f" ERROR: could not write to {output_path}: {exc}", file=sys.stderr) + return False + + elapsed = time.perf_counter() - start + + if result.returncode != 0: + print( + f" WARNING: '{label}' exited with code {result.returncode}.", + file=sys.stderr, + ) + + size_kb = output_path.stat().st_size / 1024 + print(f" Saved to {output_path} ({size_kb:.1f} KB, {elapsed:.2f}s)") + return result.returncode == 0 + + +def main() -> int: + cwd = Path.cwd() + + dir_output = cwd / "DirOutput.txt" + tree_output = cwd / "TreeOutput.txt" + + results = [ + run_command(["cmd", "/c", "dir", "/s"], dir_output, "dir /s"), + run_command(["cmd", "/c", "tree", "/F", "/A"], tree_output, "tree /F /A"), + ] + + print("\nDone." if all(results) else "\nCompleted with warnings.") + + # Only pause when run interactively (not piped / redirected) + if sys.stdin.isatty(): + input("\nPress Enter to exit...") + + return 0 if all(results) else 1 + + +if __name__ == "__main__": + sys.exit(main())