v1
This commit is contained in:
@@ -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 <DIR> .
|
||||||
|
02/20/2026 10:00 AM <DIR> ..
|
||||||
|
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.
|
||||||
@@ -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())
|
||||||
Reference in New Issue
Block a user