# fselect
Find files with SQL-like queries

[![Crates.io](https://img.shields.io/crates/v/fselect.svg)](https://crates.io/crates/fselect)
[![build](https://github.com/jhspetersson/fselect/actions/workflows/rust.yml/badge.svg)](https://github.com/jhspetersson/fselect/actions/workflows/rust.yml)

### Why use fselect?

While it doesn't tend to fully replace traditional `find` and `ls`, **fselect** has these nice features:

* SQL-like (not real SQL, but highly relaxed!) grammar easily understandable by humans
* complex queries, compare results in several directories with [subqueries](docs/usage.md#subqueries-for-in-and-exists)
* aggregate, statistics, date, and other [functions](docs/usage.md#functions)
* search within archives
* `.gitignore`, `.hgignore`, and `.dockerignore` support (experimental)
* search by width and height of images, EXIF metadata
* search by audio metadata (MP3, FLAC, Ogg Vorbis, Opus, M4A/AAC, WAV, AIFF, APE, WavPack, Musepack, Speex)
* search by extended file attributes, POSIX ACLs, and Linux capabilities
* search by file hashes
* search by MIME type
* shortcuts to common file types
* [interactive mode](docs/usage.md#interactive-mode)
* support for `plocate` and `Everything` indexes for lightning-fast search
* various output formatting (CSV, JSON, and others)

More is under way!

### Installation

#### Latest release from source

* Install [Rust with Cargo](https://www.rust-lang.org/en-US/install.html) and its dependencies to build a binary
* Run `cargo install fselect`

You can optionally build with fast, index-backed search support: [voidtools *Everything*](https://www.voidtools.com/)
on Windows (`--features everything`, then pass `--everything`) or [*plocate*](https://plocate.sesse.net/)
on Linux (`--features plocate`, then pass `--plocate`). See the
[usage guide](docs/usage.md#index-backed-search-everything--plocate) for details.

#### Debian/Ubuntu

[deb package](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect_0.10.3-1_amd64.deb)

#### Fedora/RHEL/openSUSE

[rpm package](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-0.10.3-1.x86_64.rpm)

#### Arch Linux

[AUR package](https://aur.archlinux.org/packages/fselect/), thanks to [@asm0dey](https://github.com/asm0dey)

[AUR bin package](https://aur.archlinux.org/packages/fselect-bin/), thanks to [@4censord](https://github.com/4censord)

#### NixOS

[`fselect` in `nixpkgs`](https://github.com/filalex77/nixpkgs/blob/1eced92263395896c10cea69e5f60e8be5f43aeb/pkgs/tools/misc/fselect/default.nix), thanks to [@filalex77](https://github.com/filalex77)

#### Other Linux

* x86_64: [glibc](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-x86_64-linux.gz) &middot; [musl (static)](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-x86_64-linux-musl.gz)
* aarch64: [glibc](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-aarch64-linux.gz) &middot; [musl (static)](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-aarch64-linux-musl.gz)

#### Windows 64bit

A precompiled [binary](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-x86_64-win.zip)

#### Windows via winget

* Install [winget](https://github.com/microsoft/winget-cli)
* Run `winget install -e --id fselect.fselect`

#### Windows via Chocolatey

* Install [Chocolatey](https://chocolatey.org/install)
* Run `choco install fselect`

#### Windows via Scoop

* Install [Scoop](https://scoop.sh)
* Run `scoop install fselect`

#### macOS

Precompiled binaries are available at GitHub downloads (`gunzip`, then `chmod +x fselect`):

* [Intel (x86_64)](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-x86_64-mac.gz)
* [Apple Silicon (aarch64)](https://github.com/jhspetersson/fselect/releases/download/0.10.3/fselect-aarch64-mac.gz)

#### Mac via Homebrew

* Install [brew](https://brew.sh)
* Run `brew install fselect`

#### Mac via MacPorts

* Install [MacPorts](https://www.macports.org)
* Run:
  ```
  sudo port selfupdate
  sudo port install fselect
  ```

### Usage

    fselect [ARGS] COLUMN[, COLUMN...] [from ROOT[, ROOT...]] [where EXPR] [group by COLUMNS] [having EXPR] [order by COLUMNS] [limit N] [offset N] [into FORMAT]

### Interactive mode

    fselect -i

### Documentation

[Detailed description of all the supported features.](docs/usage.md)

### Examples

Find temporary or config files (full path and size):

    fselect size, path from /home/user where name = '*.cfg' or name = '*.tmp'
    
Windows users may omit the quotes:

    fselect size, path from C:\Users\user where name = *.cfg or name = *.tmp

Or put all the arguments into the quotes like this:

    fselect "name from /home/user/tmp where size > 0"

Search within a directory name with spaces (backticks are also supported):

    fselect "name from '/home/user/dir with spaces' where size > 0"
    fselect "name from `/home/user/dir with spaces` where size > 0"

Or simply escape the single quote:

    fselect name from \'/home/user/dir with spaces\' where size gt 0

Specify the file size, get an absolute path, and add it to the results:

    cd /home/user
    fselect size, abspath from ./tmp where size gt 2g
    fselect fsize, abspath from ./tmp where size = 5m
    fselect hsize, abspath from ./tmp where size lt 8k
    fselect name, size from ./tmp where size between 5mb and 6mb
    
More complex query:

    fselect "name from /tmp where (name = *.tmp and size = 0) or (name = *.cfg and size > 1000000)"

You can use subqueries:

    fselect "name from /test1 where size > 100 and size in (select size from /test2 where name in (select name from /test3 where modified in (select modified from /test4 where size < 200)))"
    fselect "name, path, size from /data as data where exists (select * from /backup as backup where backup.name = data.name)"

A subquery can also be used in the `FROM` clause as the source of the outer query:

    fselect "src.name, src.size from (select path from /projects depth 2 where size > 100) as src where src.name like '%.rs'"
    
Aggregate functions (you can use curly braces if you want and even combine them with the regular parentheses):

    fselect "MIN(size), MAX{size}, AVG(size), SUM{size}, COUNT(*) from /home/user/Downloads"
    
Formatting functions:

    fselect "LOWER(name), UPPER(name), LENGTH(name), YEAR(modified) from /home/user/Downloads"
    
Get the year of the oldest file:

    fselect "MIN(YEAR(modified)) from /home/user"
    
Use single quotes if you need to address files with spaces:

    fselect "path from '/home/user/Misc stuff' where name != 'Some file'"
    
Regular expressions of [Rust flavor](https://docs.rs/regex/1.1.0/regex/#syntax) are supported:

    fselect name from /home/user where path =~ '.*Rust.*'
    
Negate regular expressions:

    fselect "name from . where path !=~ '^\./config'"
    
Simple globs expand automatically and work with `=` and `!=` operators:

    fselect name from /home/user where path = '*Rust*'
    
Classic LIKE:

    fselect "path from /home/user where name like '%report-2018-__-__???'"
    
Exact match operators to search with regexps disabled:

    fselect "path from /home/user where name === 'some_*_weird_*_name'"
    
Find files by date:

    fselect path from /home/user where created = 2017-05-01
    fselect path from /home/user where modified = today
    fselect path from /home/user where accessed = yesterday
    fselect "path from /home/user where modified = 'apr 1'"
    fselect "path from /home/user where modified = 'last fri'"
    
Be more specific to match all files created at an interval between 3PM and 4PM:

    fselect path from /home/user where created = '2017-05-01 15'
    
And even more specific:

    fselect path from /home/user where created = '2017-05-01 15:10'
    fselect path from /home/user where created = '2017-05-01 15:10:30'
    
Date and time intervals are possible (find everything updated since May 1st):

    fselect path from /home/user where modified gte 2017-05-01
    
Default is the current directory:

    fselect path, size where name = '*.jpg'
    
Search within multiple locations:

    fselect path from /home/user/oldstuff, /home/user/newstuff where name = '*.jpg'
    
With minimum and/or maximum depth specified (`depth` is a synonym for `maxdepth`):

    fselect path from /home/user/oldstuff depth 5 where name = '*.jpg'
    fselect path from /home/user/oldstuff mindepth 2 maxdepth 5, /home/user/newstuff depth 10 where name = '*.jpg'

Optionally follow symlinks:

    fselect path, size from /home/user symlinks where name = '*.jpg'
    
Search within archives (currently only zip-archives are supported):

    fselect path, size from /home/user archives where name = '*.jpg'
    
Or in combination:

    fselect size, path from /home/user depth 5 archives symlinks where name = '*.jpg' limit 100

Enable `.gitignore` or `.hgignore` support:

    fselect size, path from /home/user/projects gitignore where name = '*.cpp'
    fselect size, path from /home/user/projects git where name = '*.cpp'    
    fselect size, path from /home/user/projects hgignore where name = '*.py'        
    
Search by image dimensions:

    fselect CONCAT(width, 'x', height), path from /home/user/photos where width gte 2000 or height gte 2000
    
Find square images:
    
    fselect path from /home/user/Photos where width = height

Find images with a known name part but unknown extension:
    
    fselect path from /home/user/projects where name = "*RDS*" and width gte 1

Find old-school rap MP3 files:

    fselect duration, path from /home/user/music where genre = Rap and bitrate = 320 and mp3_year lt 2000  
    
Shortcuts to common file extensions:

    fselect path from /home/user where is_archive = true
    fselect path, mime from /home/user where is_audio = 1
    fselect path, mime from /home/user where is_book != false

Even simpler way of using boolean columns:

    fselect path from /home/user where is_doc
    fselect path from /home/user where is_image
    fselect path from /home/user where is_video
    
Find files with dangerous permissions:
    
    fselect mode, path from /home/user where other_write or other_exec
    fselect mode, path from /home/user where other_all
    
Simple glob-like expressions or even regular expressions in file mode are possible:
    
    fselect mode, path from /home/user where mode = '*rwx'
    fselect mode, path from /home/user where mode =~ '.*rwx$'
    
Find files by owner's uid or gid:

    fselect uid, gid, path from /home/user where uid != 1000 or gid != 1000
    
Or by owner's or group's name:

    fselect user, group, path from /home/user where user = mike or group = mike

Find special files:

    fselect name from /usr/bin where suid
    fselect path from /tmp where is_sticky
    fselect path from /tmp where is_pipe
    fselect path from /tmp where is_socket
    
Find files with xattrs, check if a particular xattr exists, or get its value:

    fselect "path, has_xattrs, has_xattr(user.test), xattr(user.test) from /home/user"
    
Include arbitrary text as columns:

    fselect "name, ' has size of ', size, ' bytes'"

Group results:

    fselect "ext, count(*) from /tmp group by ext"
    fselect "ext, count(*) from /tmp group by 1"
    fselect "mime, ext, count(*) from /tmp group by 1, 2"

Filter groups after aggregation:

    fselect "ext, count(*) from /tmp group by ext having count(*) > 10"
    fselect "sha256, count(*) from ~/Downloads group by sha256 having count(*) > 1"

Order results:

    fselect path from /tmp order by size desc, name
    fselect modified, fsize, path from ~ order by 1 desc, 3
    
Finally, limit the results:

    fselect name from /home/user/samples limit 5 
    
Format output:

    fselect size, path from /home/user limit 5 into json
    fselect size, path from /home/user limit 5 into csv
    fselect size, path from /home/user limit 5 into html

### License

MIT/Apache-2.0

---

Supported by [JetBrains IDEA](https://jb.gg/OpenSourceSupport) open source license
