A command-line file management utility for common house-keeping tasks such as deduplicating files, deleting files by extension, cleaning folders, removing empty directories, deleting hidden files, and compressing files into a ZIP archive.
The tool is built around a simple, extensible command pattern: each operation is
a self-contained command that validates its own arguments and executes against a
shared logger, with first-class support for a --dry-run mode so you can preview
every action before anything touches disk.
- Deduplicate – Find and remove duplicate files using MD5 content hashing, with optional multi-threaded scanning.
- Delete by extension – Delete files matching one or more extensions.
- Clean folder – Delete files in a folder, optionally excluding certain names.
- Delete empty folders – Remove empty directories, optionally recursively.
- Delete hidden files – Remove hidden files (dot-files on POSIX, hidden attribute on Windows).
- Compress files – Bundle files from a directory into a timestamped ZIP archive, with optional extension and name filters.
- Dry-run mode – Preview any command without modifying the file system.
- Configurable logging – Log to a file at a chosen level (
DEBUG,INFO,WARNING, ...).
- Python 3.10+ (uses
TypeAliasand other modern typing features) - Dependencies listed in requirements.txt
# Clone the repository
git clone https://github.com/neolace/file-manager.git
cd file-manager
# (Recommended) create and activate a virtual environment
python -m venv .venv
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# macOS / Linux
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txtRun the tool via main.py, selecting an operation with --command:
python main.py --command <command> [options]These options apply to every command:
| Option | Default | Description |
|---|---|---|
--command |
(required) | Command to execute (see below). |
--log |
app.log |
Path to the log file. |
--log-level |
INFO |
Logging level (DEBUG, INFO, WARNING, ...). |
--dry-run |
false |
Simulate the action without modifying the file system. |
Find duplicate files by content hash and remove the duplicates.
| Option | Required | Description |
|---|---|---|
--directory |
Yes | Target directory to scan (recursively). |
--max-workers |
No | Worker threads for hashing (default 1). |
python main.py --command deduplicate --directory ./photos --max-workers 4 --dry-runDelete files matching the given extensions.
| Option | Required | Description |
|---|---|---|
--path |
Yes | Target directory. |
--extensions |
Yes | Comma-separated list of extensions (e.g. tmp,log). |
python main.py --command delete_by_extension --path ./downloads --extensions tmp,logDelete files within a folder, optionally excluding certain names.
| Option | Required | Description |
|---|---|---|
--path |
Yes | Target directory. |
--excluded-names |
No | Comma-separated names to exclude from cleaning. |
python main.py --command clean_folder --path ./tmp --excluded-names keep.txt,.gitkeepDelete empty folders, optionally recursively.
| Option | Required | Description |
|---|---|---|
--path |
Yes | Target directory. |
--recursive |
No | Recurse into subdirectories when set. |
python main.py --command delete_empty --path ./project --recursivedelete_hidden_files
Delete hidden files (dot-files on POSIX; hidden attribute on Windows).
| Option | Required | Description |
|---|---|---|
--path |
Yes | Target directory. |
--excluded-names |
No | Comma-separated names to exclude. |
python main.py --command delete_hidden_files --path ./repo --dry-runCompress files from a directory into a timestamped ZIP archive. The archive is
created in the parent directory as <folder-name>_<YYYYMMDD_HHMMSS>.zip.
| Option | Required | Description |
|---|---|---|
--path |
Yes | Directory whose files should be compressed. |
--extensions |
No | Comma-separated extensions to include (all files if omitted). |
--excluded-names |
No | Comma-separated names to exclude. |
python main.py --command compress_files --path ./logs --extensions log,txtNote: The
movecommand is registered but its execution is not yet implemented.
Add --dry-run to any command to log exactly what would happen without deleting,
moving, or writing anything:
python main.py --command deduplicate --directory ./photos --dry-runmain.py # Entry point: argument parsing, command dispatch, logging
requirements.txt # Python dependencies
config/ # Configuration and constants
settings.py # Central Config (buffer sizes, messages, defaults)
fm_FileType.py # Supported file types
LogLevel.py # Log level definitions
functions/ # Command implementations
CommandType.py # Enum of supported commands
CompressFilesCommand.py
CleanFolderCommand.py
DeleteByExtensionCommand.py
DeleteEmptyFoldersCommand.py
DeleteHiddenFilesCommand.py
FileDeduplicator.py
MoveCommand.py
ProcessFilesCommandBase.py # Shared base for file-processing commands
exceptions.py # Custom exception hierarchy
Interface/
CommandInterface.py # Command contract (validate/execute/description)
utils/ # Helpers: argument parsing, validation, logging, filtering
CommandInterfacedefines the contract every command implements:validate(args),execute(args, logger), and adescriptionproperty.CommandRegistrymaps command names to their implementations.CommandHandlerlooks up the command, validates arguments, executes it, and maps known exceptions to friendly log messages.ProcessFilesCommandBaseprovides shared file-walking logic for commands that operate on individual files.
- Add a new value to
CommandTypein functions/CommandType.py. - Implement a class that satisfies
CommandInterface(or extendProcessFilesCommandBase). - Register it in
CommandRegistry.COMMAND_MAPin main.py. - Add any new CLI arguments in utils/parse_arguments.py.
Tests use pytest:
pytest
# with coverage
pytest --covThe project ships with common tooling in requirements.txt:
- Formatting:
black,isort - Linting:
flake8,pylint - Type checking:
mypy - Testing:
pytest,pytest-cov
black .
isort .
flake8
mypy .