Metadata-Version: 2.4
Name: fuzzycp
Version: 0.4.0
Summary: CLI for fuzzy matching filenames and copying or moving the best matches.
License-Expression: MIT
Project-URL: Homepage, https://github.com/rsnemmen/fuzzy_cp
Project-URL: Repository, https://github.com/rsnemmen/fuzzy_cp
Project-URL: Issues, https://github.com/rsnemmen/fuzzy_cp/issues
Keywords: cli,copy,move,fuzzy-matching,rapidfuzz
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: rapidfuzz
Requires-Dist: termcolor
Requires-Dist: humanize
Requires-Dist: tqdm
Dynamic: license-file

# `fuzzycp`: Fuzzy file operations (`mv`, `cp`) 

fuzzycp performs file operations such as copy and move on files whose filenames match a list. The matching is fuzzy, in other words there is an approximate match. 

![fuzzycp Demo GIF](demo.gif)

## The problem of fuzzy matching

In order to understand what that means, here is a concrete example. 

Suppose you have a file `names.txt` containing a list of names you want to match against. Let's say the content of this file is:

```
1. The Legend of Zelda: Ocarina of Time
2. Super Mario 64
3. Mario Kart 64
4. GoldenEye 007
5. Super Smash Bros.
```

This is a random example—the top five games released for the Nintendo 64 console. Now, you have a directory with thousands of files, and you want to copy to another directory only the files that are the best-match to the names in the above list. Here are some examples of files in that directory:

```
'Spider-Man (U) [!].v64'
'StarCraft 64 (U) [!].v64'
'Starfox 64 1.1 (U).v64'  
'Starshot - Space Circus Fever (U) [!].z64'
'Star Wars - Rogue Squadron (U) [!].v64'
'Star Wars - Shadows of the Empire (U) (V1.2) [!].v64'
'Star Wars Episode I - Battle for Naboo (U) [!].v64'
'Star Wars Episode I - Racer (U) [!].v64'
'Stunt Racer 64 (U) [!].z64'
'Super Bowling 64 (U) [!].z64'
'Supercross 2000 (U) [!].z64'
'Superman (U) (M3) [!].z64'
'Super Mario 64 (U) [!].v64'
```

For the sake of our example, the content of these files is meaningless (let's say they have the metadata for those games). Notice that there will be no exact match between the names in `names.txt` and the actual filenames. They could have different casing, missing text, extra letters etc. This is where the power of fuzzy matching shines: *you don't need an exact match*.

## The solution

Normally people would do this sort of thing by manually selecting file by file and copying them. Not anymore. Here is how you solve this using `fuzzycp`. First `cd` to the directory containing the files.

Copy only the best-matching files to directory `dest/directory`:

    fuzzycp names.txt -c dest/directory

![](example.png)

Move only the best-matching files to directory `dest/directory`:

    fuzzycp names.txt -m dest/directory

Print the best-matching files and the matching score:

    fuzzycp names.txt 

Print the best-matching files, and the space they occupy:

    fuzzycp names.txt -s


## Installation

### Install from PyPI

```shell
pip install fuzzycp
```

This installs both the `fuzzycp` package and the `fuzzycp` command-line entry point.
The published package targets Python 3.9+.

You can also run it as a module:

```shell
python -m fuzzycp names.txt
```

### Install the standalone binary

```shell
curl -fsSL https://raw.githubusercontent.com/rsnemmen/fuzzy_cp/main/install.sh | sh
```

This downloads the pre-built binary for your platform (macOS arm64/x86_64, Linux x86_64) and installs it to `~/.local/bin/fuzzycp`.

## How it works

fuzzycp uses the [RapidFuzz library—a fast, lightweight C++ library—](https://github.com/rapidfuzz/RapidFuzz)for fuzzy matching, i.e. measuring how similar two strings (or other sequences) are and finding the best match in a collection. 

Internally, fuzzycp compares the names using the `WRatio` scorer, which internally tries `ratio`, `partial_ratio`, `token_sort_ratio`, and `token_set_ratio` and picks the highest — making it robust for partial or reordered names. The scorer can easily be swapped in `file_matching()` for any other RapidFuzz scorer.

## Build and release

Build the source distribution and wheel from a clean checkout:

```shell
python -m pip install --upgrade build twine
rm -rf build dist src/*.egg-info
python -m build
python -m twine check dist/*
```

This creates:

- `dist/fuzzycp-<version>.tar.gz`
- `dist/fuzzycp-<version>-py3-none-any.whl`

To publish to PyPI:

```shell
python -m twine upload dist/*
```

Recommended maintainer checklist:

1. Bump the version in `pyproject.toml`.
2. Build and check the release artifacts with the commands above.
3. Upload `dist/*` to PyPI.
4. Push a version tag (`v*`) if you also want the GitHub Actions workflow to publish standalone binaries.

## TBD

- [ ] Homebrew recipe
- [x] move files functionality
- [ ] windows standalone EXE
- [ ] add multi-disk mode
