1100 lines
91 KiB
Markdown
1100 lines
91 KiB
Markdown
# fselect
|
|
|
|
Find files with SQL-like queries.
|
|
|
|
[Basic usage](#basic-usage)
|
|
[Restrictions](#its-not-a-real-sql)
|
|
[Columns and fields](#columns-and-fields)
|
|
[File naming terminology](#file-naming-terminology)
|
|
[Functions](#functions)
|
|
[File size units](#file-size-units)
|
|
[Search roots](#search-roots)
|
|
[Operators](#operators)
|
|
[Arithmetic operators](#arithmetic-operators)
|
|
[Subqueries in the FROM clause](#subqueries-in-the-from-clause)
|
|
[Subqueries for IN and EXISTS](#subqueries-for-in-and-exists)
|
|
[Date and time specifiers](#date-and-time-specifiers)
|
|
[Regular expressions](#regular-expressions)
|
|
[MIME and file types](#mime-and-file-types)
|
|
[Audio support](#audio-support)
|
|
[File hashes](#file-hashes)
|
|
[Output formats](#output-formats)
|
|
[Configuration file](#configuration-file)
|
|
[Bash completion](#bash-completion)
|
|
[Command-line arguments](#command-line-arguments)
|
|
[Index-backed search](#index-backed-search-everything--plocate)
|
|
[Interactive mode](#interactive-mode)
|
|
[Environment variables](#environment-variables)
|
|
[Exit values](#exit-values)
|
|
|
|
### Basic usage
|
|
|
|
fselect [ARGS] COLUMN[, COLUMN...] [from ROOT[, ROOT...]] [where EXPR] [group by COLUMNS] [order by COLUMNS] [limit N] [offset N] [into FORMAT]
|
|
|
|
You write an SQL-like query, that's it.
|
|
|
|
**fselect** command itself is like a first keyword (`select`, i.e., *file select*).
|
|
But if you put one more `select` behind occasionally, that's not a problem.
|
|
|
|
Next you put columns you are interested in. It could be a file name or path, size, modification date, etc.
|
|
See the full list of possible columns. You can add columns with arbitrary text (put in quotes if it contains spaces).
|
|
A few functions (aggregating and formatting) are there for your service. You can use arithmetic expressions when it makes sense.
|
|
|
|
Where to search? Specify with `from` keyword. You can list one or more directories separated with comma.
|
|
If you leave the `from`, then current directory will be processed.
|
|
|
|
What to search? Use `where` with any number of conditions.
|
|
|
|
Group results with `group by` followed by one or more columns. Like `order by`, this clause
|
|
accepts positional numeric shortcuts that refer to columns from the `select` list, for example
|
|
`group by 1` or `group by 1, 2`. An aggregate function in the `select` list is not required:
|
|
`select ext from /home/user group by ext` returns one row per distinct extension, like
|
|
`SELECT DISTINCT` in SQL.
|
|
|
|
Order results like in real SQL with `order by`. All columns are supported for ordering by,
|
|
as well as `asc`/`desc` parameters and positional numeric shortcuts.
|
|
|
|
Limiting search results is possible with `limit` and `offset`. Formatting options are supported with `into` keyword.
|
|
|
|
If you want to use operators containing `>` or `<`,
|
|
put the whole query into the double quotes.
|
|
This will protect a query from the shell and output redirection.
|
|
The same applies to queries with parentheses or `*`, `?` and other special shell
|
|
metacharacters.
|
|
|
|
It's ok to use any metacharacters in interactive mode.
|
|
|
|
### It's not a real SQL
|
|
|
|
Directories to search at are listed with comma separators.
|
|
In a real SQL, such syntax would make a cross-product. Here it means just search at A, next at B, and so on.
|
|
|
|
You can use curly braces instead of the regular parentheses! This helps to avoid a few of the shell pitfalls a little bit.
|
|
Functions with no arguments don't require parentheses at all.
|
|
|
|
String literals don't really need quotes.
|
|
You will need to put them just in case you query something with spaces inside.
|
|
And yes, you should use quotes for glob-patterns or regular expressions in the query
|
|
on Linux or macOS to prevent parameter expansion from the shell.
|
|
If you are on Windows, feel free to omit most of the quotes.
|
|
|
|
Commas for column separation aren't needed as well. Column aliasing (with or without `as` keyword) is not supported.
|
|
|
|
`where` section can contain short syntax conditions for boolean columns (like `is_audio` or `other_write`).
|
|
|
|
`into` keyword specifies output format, not output table.
|
|
|
|
Joins and unions are not supported (yet?).
|
|
Subqueries have only limited support: in `IN` / `EXISTS` predicates, and as the source of a `FROM` clause.
|
|
|
|
### Columns and fields
|
|
|
|
| Column | Meaning | Comment |
|
|
|----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
|
|
| `name` | Returns the name (with extension) of the file | |
|
|
| `filename` or `fname` | Returns the file name without extension | |
|
|
| `extension` or `ext` | Returns the extension of the file | |
|
|
| `path` | Returns the relative path of the file | |
|
|
| `abspath` | Returns the absolute path of the file | |
|
|
| `directory` or `dirname` or `dir` | Returns the directory of the file | |
|
|
| `absdir` | Returns the absolute directory of the file | |
|
|
| `size` | Returns the size of the file in bytes | |
|
|
| `fsize` or `hsize` | Returns the size of the file accompanied with the unit | |
|
|
| `uid` | Returns the UID of the owner | |
|
|
| `gid` | Returns the GID of the owner's group | |
|
|
| `accessed` | Returns the time the file was last accessed (YYYY-MM-DD HH:MM:SS) | |
|
|
| `created` | Returns the file creation date (YYYY-MM-DD HH:MM:SS) | Windows, macOS, and Linux (kernel 4.11+ with ext4/btrfs/XFS) |
|
|
| `modified` | Returns the time the file was last modified (YYYY-MM-DD HH:MM:SS) | |
|
|
| `atime` | Returns the last access time as a Unix timestamp (seconds since epoch) | Available only on Unix |
|
|
| `atime_nsec` | Returns the nanosecond component of the last access time | Available only on Unix |
|
|
| `mtime` | Returns the last modification time as a Unix timestamp (seconds since epoch) | Available only on Unix |
|
|
| `mtime_nsec` | Returns the nanosecond component of the last modification time | Available only on Unix |
|
|
| `ctime` | Returns the last status change time as a Unix timestamp (seconds since epoch) | Available only on Unix |
|
|
| `ctime_nsec` | Returns the nanosecond component of the last status change time | Available only on Unix |
|
|
| `is_dir` | Returns a boolean signifying whether the file path is a directory | |
|
|
| `is_file` | Returns a boolean signifying whether the file path is a file | |
|
|
| `is_symlink` | Returns a boolean signifying whether the file path is a symlink | |
|
|
| `link_target` or `symlink_target` | Returns the target path of the symlink, or an empty value if the file is not a symlink | |
|
|
| `is_broken_symlink` or `is_broken_link` | Returns a boolean signifying whether the file path is a symlink whose target does not exist | |
|
|
| `is_pipe` or `is_fifo` | Returns a boolean signifying whether the file path is a FIFO or pipe file | |
|
|
| `is_char` or `is_character` | Returns a boolean signifying whether the file path is a character device or character special file | |
|
|
| `is_block` | Returns a boolean signifying whether the file path is a block or block special file | |
|
|
| `is_socket` | Returns a boolean signifying whether the file path is a socket file | |
|
|
| `is_hidden` | Returns a boolean signifying whether the file is a hidden file (e.g., files that start with a dot on *nix) | |
|
|
| `has_xattrs` | Returns a boolean signifying whether the file has extended attributes or alternate data streams on Windows | |
|
|
| `xattr_count` | Returns the count of extended attributes on the file or alternate data streams on Windows | |
|
|
| `extattrs` | Returns the extended file attributes as a string of flag letters (chattr/lsattr flags on Linux, NTFS attribute letters on Windows) | Available only on Linux and Windows |
|
|
| `has_extattrs` | Returns a boolean signifying whether the file has any extended file attributes set | Available only on Linux and Windows |
|
|
| `acl` | Returns all ACL entries in standard form (POSIX on Linux, DACL on Windows) | Available only on Linux and Windows |
|
|
| `has_acl` | Returns a boolean signifying whether the file has POSIX ACL entries beyond standard Unix permissions or Windows explicit ACEs | Available only on Linux and Windows |
|
|
| `default_acl` | Returns all default ACL entries in standard form (default POSIX ACLs on Linux, inheritable ACEs on Windows) | Available only on Linux and Windows |
|
|
| `has_default_acl` | Returns a boolean signifying whether the directory has default ACL entries (default POSIX ACLs on Linux, inheritable ACEs on Windows) | Available only on Linux and Windows |
|
|
| `has_capabilities` or `has_caps` | Returns a boolean signifying whether the file has capabilities | Available only on Linux |
|
|
| `capabilities` or `caps` | Returns a string describing Linux capabilities assigned to a file | Available only on Linux |
|
|
| `device` | Returns the code of device the file is stored on | Available only on Unix |
|
|
| `rdev` | Returns the device ID for special files (character and block devices) | Available only on Unix |
|
|
| `inode` | Returns the number of inode | Available only on Linux |
|
|
| `blocks` | Returns the number of blocks (512 bytes) the file occupies | Available only on Linux |
|
|
| `blksize` or `block_size` | Returns the preferred block size for filesystem I/O in bytes | Available only on Unix |
|
|
| `hardlinks` | Returns the number of hardlinks of the file | Available only on Linux |
|
|
| `mode` | Returns the permissions of the owner, group, and everybody (similar to the first field in `ls -la`) | |
|
|
| `user` | Returns the name of the owner for this file | Available only on *nix platforms with `users` feature enabled |
|
|
| `user_read` | Returns a boolean signifying whether the file can be read by the owner | |
|
|
| `user_write` | Returns a boolean signifying whether the file can be written by the owner | |
|
|
| `user_exec` | Returns a boolean signifying whether the file can be executed by the owner | |
|
|
| `user_all` | Returns a boolean signifying whether the file can be fully accessed by the owner | |
|
|
| `group` | Returns the name of the owner's group for this file | Available only on *nix platforms with `users` feature enabled |
|
|
| `group_read` | Returns a boolean signifying whether the file can be read by the owner's group | |
|
|
| `group_write` | Returns a boolean signifying whether the file can be written by the owner's group | |
|
|
| `group_exec` | Returns a boolean signifying whether the file can be executed by the owner's group | |
|
|
| `group_all` | Returns a boolean signifying whether the file can be fully accessed by the group | |
|
|
| `other_read` | Returns a boolean signifying whether the file can be read by others | |
|
|
| `other_write` | Returns a boolean signifying whether the file can be written by others | |
|
|
| `other_exec` | Returns a boolean signifying whether the file can be executed by others | |
|
|
| `other_all` | Returns a boolean signifying whether the file can be fully accessed by the others | |
|
|
| `suid` or `is_suid` | Returns a boolean signifying whether the file permissions have a SUID bit set | |
|
|
| `sgid` or `is_sgid` | Returns a boolean signifying whether the file permissions have a SGID bit set | |
|
|
| `sticky` or `is_sticky` | Returns a boolean signifying whether the file permissions have a sticky bit set | |
|
|
| `width` | Returns the number of pixels along the width of the photo or MP4 file | |
|
|
| `height` | Returns the number of pixels along the height of the photo or MP4 file | |
|
|
| `mime` | Returns MIME type of the file | |
|
|
| `is_binary` | Returns a boolean signifying whether the file has binary contents | |
|
|
| `is_text` | Returns a boolean signifying whether the file has text contents | |
|
|
| `line_count` | Returns a number of lines in a text file | |
|
|
| `word_count` or `words` | Returns the number of whitespace-separated words in a text file | |
|
|
| `char_count` or `chars` | Returns the number of characters in a text file | |
|
|
| `encoding` | Returns the detected text encoding of the file (e.g., ASCII, UTF-8, UTF-16LE), or an empty value for binary files | |
|
|
| `has_bom` | Returns a boolean signifying whether the file begins with a byte-order mark (BOM) | |
|
|
| `line_ending` or `line_endings` or `eol` | Returns the line ending style of a text file (LF, CRLF, CR, Mixed, or empty) | |
|
|
| `exif_datetime` | Returns date and time of taken photo | |
|
|
| `exif_datetime_original` or `exif_dto` | Returns original date and time when the photo was taken | |
|
|
| `exif_altitude` or `exif_alt` | Returns GPS altitude of taken photo | |
|
|
| `exif_latitude` or `exif_lat` | Returns GPS latitude of taken photo | |
|
|
| `exif_longitude` or `exif_lng` or `exif_lon` | Returns GPS longitude of taken photo | |
|
|
| `exif_make` | Returns name of the camera manufacturer | |
|
|
| `exif_model` | Returns camera model | |
|
|
| `exif_software` | Returns software name with which the photo was taken | |
|
|
| `exif_version` | Returns the version of EXIF metadata | |
|
|
| `exif_exposure_time` or `exif_exptime` | Returns exposure time of the photo taken | |
|
|
| `exif_aperture` | Returns aperture value of the photo taken | |
|
|
| `exif_shutter_speed` | Returns shutter speed of the photo taken | |
|
|
| `exif_f_number` or `exif_f_num` | Returns F-number of the photo taken | |
|
|
| `exif_iso_speed` or `exif_iso` | Returns ISO speed of the photo taken (EXIF 2.3 ISOSpeed tag) | |
|
|
| `exif_sensitivity` or `exif_photo_sensitivity` | Returns photographic sensitivity (ISO) of the photo taken | |
|
|
| `exif_focal_length` or `exif_focal_len` | Returns focal length of the photo taken | |
|
|
| `exif_lens_make` | Returns lens manufacturer used to take the photo | |
|
|
| `exif_lens_model` | Returns lens model used to take the photo | |
|
|
| `exif_description` or `exif_desc` | Returns image description from EXIF metadata | |
|
|
| `exif_artist` | Returns the artist or photographer name | |
|
|
| `exif_copyright` | Returns the copyright information | |
|
|
| `exif_orientation` | Returns the image orientation | |
|
|
| `exif_flash` | Returns the flash status when the photo was taken | |
|
|
| `exif_color_space` | Returns the color space of the image | |
|
|
| `exif_exposure_program` or `exif_exp_program` | Returns the exposure program used | |
|
|
| `exif_exposure_bias` or `exif_exp_bias` | Returns the exposure bias value | |
|
|
| `exif_white_balance` or `exif_wb` | Returns the white balance mode | |
|
|
| `exif_metering_mode` | Returns the metering mode | |
|
|
| `exif_scene_type` or `exif_scene` | Returns the scene capture type | |
|
|
| `exif_contrast` | Returns the contrast setting | |
|
|
| `exif_saturation` | Returns the saturation setting | |
|
|
| `exif_sharpness` | Returns the sharpness setting | |
|
|
| `exif_body_serial` or `exif_serial` | Returns the camera body serial number | |
|
|
| `exif_lens_serial` | Returns the lens serial number | |
|
|
| `exif_user_comment` or `exif_comment` | Returns the user comment from EXIF metadata | |
|
|
| `exif_image_width` or `exif_width` | Returns the image width from EXIF metadata | |
|
|
| `exif_image_height` or `exif_height` | Returns the image height from EXIF metadata | |
|
|
| `exif_max_aperture` | Returns the max aperture value of the lens | |
|
|
| `exif_digital_zoom` or `exif_dzoom` | Returns the digital zoom ratio | |
|
|
| `mp3_title` or `title` | Returns the title of the audio file taken from the file's metadata | |
|
|
| `mp3_album` or `album` | Returns the album name of the audio file taken from the file's metadata | |
|
|
| `mp3_artist` or `artist` | Returns the artist of the audio file taken from the file's metadata | |
|
|
| `mp3_genre` or `genre` | Returns the genre of the audio file taken from the file's metadata | |
|
|
| `mp3_comment` or `comment` | Returns the comment of the audio file taken from the file's metadata | |
|
|
| `mp3_track` or `track` | Returns the track number of the audio file taken from the file's metadata (e.g., 4 or 4/9) | |
|
|
| `mp3_disc` or `disc` | Returns the disc number (part of a set) of the audio file taken from the file's metadata (e.g., 1 or 1/2) | |
|
|
| `mp3_year` or `audio_year` | Returns the year of the audio file taken from the file's metadata | |
|
|
| `mp3_freq` or `freq` | Returns the sampling rate of the audio file in Hz | |
|
|
| `mp3_bitrate` or `bitrate` | Returns the bitrate of the audio file in kbps | |
|
|
| `duration` | Returns the duration in seconds of an audio file, or an MP4/Matroska/WebM video | |
|
|
| `is_shebang` | Returns a boolean signifying whether the file starts with a shebang (#!) | |
|
|
| `is_git_repo` | Returns a boolean signifying whether the directory contains a `.git` subdirectory or file | |
|
|
| `is_git_tracked` or `git_tracked` | Returns a boolean signifying whether the file is tracked by git | Requires the `git` feature (enabled by default) |
|
|
| `is_gitignored` or `is_git_ignored` | Returns a boolean signifying whether the file is ignored by git | Requires the `git` feature (enabled by default) |
|
|
| `git_status` | Returns the git status of the file (`clean`, `modified`, `staged`, `untracked`, `conflicted`, or `ignored`) | Requires the `git` feature (enabled by default) |
|
|
| `git_branch` | Returns the current branch of the git repository containing the file | Requires the `git` feature (enabled by default) |
|
|
| `git_last_commit_hash` or `git_commit_hash` | Returns the hash of the last commit that touched the file | Requires the `git` feature (enabled by default) |
|
|
| `git_last_commit_date` or `git_commit_date` | Returns the date of the last commit that touched the file | Requires the `git` feature (enabled by default) |
|
|
| `git_last_commit_author` or `git_commit_author` | Returns the author of the last commit that touched the file | Requires the `git` feature (enabled by default) |
|
|
| `is_empty` | Returns a boolean signifying whether the file is empty or the directory is empty | |
|
|
| `is_archive` | Returns a boolean signifying whether the file is an archival file | [default extensions](#ext_archive) |
|
|
| `is_audio` | Returns a boolean signifying whether the file is an audio file | [default extensions](#ext_audio) |
|
|
| `is_book` | Returns a boolean signifying whether the file is a book | [default extensions](#ext_book) |
|
|
| `is_doc` | Returns a boolean signifying whether the file is a document | [default extensions](#ext_doc) |
|
|
| `is_font` | Returns a boolean signifying whether the file is a font | [default extensions](#ext_font) |
|
|
| `is_image` | Returns a boolean signifying whether the file is an image | [default extensions](#ext_image) |
|
|
| `is_source` | Returns a boolean signifying whether the file is source code | [default extensions](#ext_source) |
|
|
| `is_video` | Returns a boolean signifying whether the file is a video file | [default extensions](#ext_video) |
|
|
| `sha1` | Returns SHA-1 digest of a file | |
|
|
| `sha2_256` or `sha256` | Returns SHA2-256 digest of a file | |
|
|
| `sha2_512` or `sha512` | Returns SHA2-512 digest of a file | |
|
|
| `sha3_512` or `sha3` | Returns SHA-3 digest of a file | |
|
|
|
|
### File naming terminology
|
|
|
|
Let's see how all these are different:
|
|
|
|
fselect abspath, absdir, path, dir, name, filename, ext from /home/user/projects where is_file
|
|
|
|
| Column | Value | Comment |
|
|
|------------|----------------------------------------------|----------------------------------------------------------------|
|
|
| `abspath` | /home/user/projects/foobar/content/readme.md | Absolute path includes everything |
|
|
| `absdir` | /home/user/projects/foobar/content | Absolute directory includes everything except the last segment |
|
|
| `path` | foobar/content/readme.md | Path is relative to the search root `/home/user/projects` |
|
|
| `dir` | foobar/content | Relative directory |
|
|
| `name` | readme.md | `name` = `filename` + `ext` |
|
|
| `filename` | readme |
|
|
| `ext` | md |
|
|
|
|

|
|
|
|
### Functions
|
|
|
|
#### Aggregate functions
|
|
|
|
Queries using these functions return only one result row.
|
|
|
|
| Function | Meaning | Example |
|
|
|---------------------------|---------------------------------------------------------------|------------------------------------------------------|
|
|
| AVG | Average of all values | `select avg(size) from /home/user/Downloads` |
|
|
| COUNT | Number of rows; `count(col)` skips rows with empty values | `select count(*) from /home/user/Downloads` |
|
|
| MAX | Maximum value (numeric, date, or string) | `select max(size) from /home/user/Downloads` |
|
|
| MIN | Minimum value (numeric, date, or string) | `select min(size) from /home/user where size gt 0` |
|
|
| SUM | Sum of all values | `select sum(size) from /home/user/Downloads` |
|
|
| STDDEV_POP, STDDEV or STD | Population standard deviation, the square root of variance | `select stddev_pop(size) from /home/user/Downloads` |
|
|
| STDDEV_SAMP | Sample standard deviation, the square root of sample variance | `select stddev_samp(size) from /home/user/Downloads` |
|
|
| VAR_POP or VARIANCE | Population variance | `select var_pop(size) from /home/user/Downloads` |
|
|
| VAR_SAMP | Sample variance | `select var_samp(size) from /home/user/Downloads` |
|
|
|
|
#### Date functions
|
|
|
|
Used mostly for formatting results.
|
|
|
|
| Function | Meaning | Example |
|
|
|-------------------------------------|--------------------------------------------------------|--------------------------------------------------------------------------|
|
|
| CURRENT_DATE or CUR_DATE or CURDATE | Returns current date | `select modified, path where modified = CURDATE()` |
|
|
| CURRENT_TIME or CUR_TIME or CURTIME | Returns current local time (HH:MM:SS) | `select CURRENT_TIME()` |
|
|
| CURRENT_TIMESTAMP or NOW | Returns current local timestamp (YYYY-MM-DD HH:MM:SS) | `select NOW()` |
|
|
| DAY | Extract day of the month | `select day(modified) from /home/user/Downloads` |
|
|
| MONTH | Extract month of the year | `select month(name) from /home/user/Downloads` |
|
|
| YEAR | Extract year of the date | `select year(name) from /home/user/Downloads` |
|
|
| DOW or DAYOFWEEK | Returns day of the week (1 - Sunday, 2 - Monday, etc.) | `select name, modified, dow(modified) from /home/user/projects/FizzBuzz` |
|
|
| DAYNAME | Returns the name of the day of the week | `select dayname(modified) from /home/user/Downloads` |
|
|
| DOY or DAYOFYEAR | Returns the day of the year (1-366) | `select dayofyear(modified) from /home/user/Downloads` |
|
|
| DATE_ADD or DATEADD | Add days to a date | `select "date_add(modified, 30) from /home/user"` |
|
|
| DATE_SUB or DATESUB | Subtract days from a date | `select "date_sub(modified, 7) from /home/user"` |
|
|
| DATE_DIFF or DATEDIFF | Number of days between two dates | `select "date_diff(modified, created) from /home/user"` |
|
|
| FROM_UNIXTIME | Convert a Unix timestamp to a datetime string | `select "from_unixtime(mtime) from /home/user"` |
|
|
| LAST_DAY or LAST_DATE | Last day of the month for a given date | `select "last_day(modified) from /home/user"` |
|
|
| EXTRACT | Extract a date/time part (year, quarter, month, week, day, hour, minute, second, dow, isodow, doy, epoch) — unit must be quoted | `select "extract('year', modified) from /home/user"` |
|
|
| DATE_TRUNC or DATETRUNC | Truncate a date to a unit (year, quarter, month, week, day, hour, minute, second) — unit must be quoted | `select "date_trunc('month', modified) from /home/user"` |
|
|
|
|
#### User functions
|
|
|
|
These are only available on Unix platforms when `users` feature has been enabled during compilation.
|
|
|
|
| Function | Meaning | Example |
|
|
|----------------|----------------------------|--------------------------|
|
|
| CURRENT_UID | Current real UID | `select CURRENT_UID()` |
|
|
| CURRENT_USER | Current real UID's name | `select CURRENT_USER()` |
|
|
| CURRENT_GID | Current primary GID | `select CURRENT_GID()` |
|
|
| CURRENT_GROUP | Current primary GID's name | `select CURRENT_GROUP()` |
|
|
|
|
#### Xattr functions
|
|
|
|
Used to check if a particular xattr exists or to get its value.
|
|
Supported platforms are Linux, macOS, FreeBSD, and NetBSD.
|
|
|
|
| Function | Meaning | Example |
|
|
|------------------------------|----------------------------------------------------------------------|---------------------------------------------------------------------|
|
|
| HAS_XATTR | Check if xattr exists | `select "name, has_xattr(user.test) from /home/user"` |
|
|
| XATTR | Get value of xattr | `select "name, xattr(user.test) from /home/user"` |
|
|
| HAS_EXTATTR | Check if a specific extended file attribute flag is set (Linux and Windows) | `select "name from / where has_extattr('i')"` |
|
|
| HAS_ACL_ENTRY | Check if a specific ACL entry exists (Linux and Windows) | `select "name from /data where has_acl_entry('user:john')"` |
|
|
| ACL_ENTRY | Get permissions of a specific ACL entry (Linux and Windows) | `select "name, acl_entry('group:staff') from /data"` |
|
|
| HAS_DEFAULT_ACL_ENTRY | Check if a specific default ACL entry exists (Linux and Windows) | `select "name from /data where has_default_acl_entry('user:john')"` |
|
|
| DEFAULT_ACL_ENTRY | Get permissions of a specific default ACL entry (Linux and Windows) | `select "name, default_acl_entry('group:staff') from /data"` |
|
|
| HAS_CAPABILITY or HAS_CAP | Check if given Linux capability exists for the file | `select "name, has_cap('cap_bpf') from /home/user"` |
|
|
|
|
#### ACLs
|
|
|
|
**fselect** can read and display Access Control Lists on both Linux and Windows.
|
|
|
|
##### POSIX ACLs (Linux)
|
|
|
|
On Linux, **fselect** reads POSIX Access Control Lists stored as `system.posix_acl_access` or `system.posix_acl_default`
|
|
extended attributes. It is useful for auditing file permissions beyond the standard Unix owner/group/other model.
|
|
|
|
The `has_acl` field returns true when a file has extended ACL entries (named users, named groups,
|
|
or a mask entry) beyond the basic owner/group/other permissions.
|
|
|
|
The `acl` field returns all ACL entries in standard `getfacl`-like format, comma-separated:
|
|
`user::rwx,user:john:rw-,group::r-x,group:staff:r--,mask::rwx,other::r--`
|
|
|
|
Use `has_acl_entry` and `acl_entry` to query specific entries. The entry specifier uses the format
|
|
`type:qualifier` where type is `user` (or `u`), `group` (or `g`), `mask` (or `m`), or `other` (or `o`).
|
|
An empty qualifier refers to the owning user/group. Examples:
|
|
|
|
fselect name from /data where has_acl = true
|
|
fselect "name, acl from /data where has_acl = true"
|
|
fselect "name from /data where has_acl_entry('user:john')"
|
|
fselect "name, acl_entry('group:staff') from /data"
|
|
|
|
When the `users` feature is enabled, uid/gid values are resolved to usernames/group names.
|
|
Otherwise, numeric IDs are used in the output.
|
|
|
|
##### Windows DACLs
|
|
|
|
On Windows, **fselect** reads the Discretionary Access Control List (DACL) via the Win32 Security API.
|
|
Only explicit (non-inherited) ACEs are shown.
|
|
|
|
The `has_acl` field returns true when a file has at least one explicit (non-inherited) ACE in its DACL.
|
|
|
|
The `acl` field returns all explicit ACEs as comma-separated entries in the format
|
|
`type:trustee:permissions`, where:
|
|
|
|
- **type** is `allow` or `deny`
|
|
- **trustee** is the resolved account name (e.g., `BUILTIN\Administrators`, `NT AUTHORITY\SYSTEM`)
|
|
- **permissions** is one of `full`, `modify`, `rx`, `read`, `write`, or a hex value for non-standard masks
|
|
|
|
Example output: `allow:BUILTIN\Administrators:full,allow:NT AUTHORITY\SYSTEM:full,allow:BUILTIN\Users:rx`
|
|
|
|
The `default_acl` and `has_default_acl` fields report a directory's *inheritable* ACEs (those
|
|
carrying the object- or container-inherit flag), which are the Windows analogue of POSIX default
|
|
ACLs — the entries that propagate to newly created child objects.
|
|
|
|
Use `has_acl_entry` and `acl_entry` (and their `default_acl_entry` counterparts) to query a
|
|
single trustee. The argument is matched against the trustee either as a full `DOMAIN\Name` or as
|
|
a bare account name, case-insensitively:
|
|
|
|
fselect name from C:\ where has_acl = true
|
|
fselect "name, acl from C:\Users where has_acl = true"
|
|
fselect "name, default_acl from C:\Windows where is_dir = true"
|
|
fselect "name from C:\data where has_acl_entry('Administrators')"
|
|
fselect "name, acl_entry('BUILTIN\Users') from C:\data"
|
|
|
|
#### Extended file attributes
|
|
|
|
**fselect** can read and query extended file attributes (also known as file flags). On Linux these
|
|
are the flags managed with `chattr` and displayed with `lsattr`, read via the `FS_IOC_GETFLAGS`
|
|
ioctl on ext2/ext3/ext4, btrfs, and other supporting filesystems. On Windows these are the NTFS
|
|
file attributes.
|
|
|
|
On Linux the `extattrs` field returns a string of flag letters for each set attribute, using the
|
|
same single-letter codes as `lsattr`/`chattr`:
|
|
`s` (secure deletion), `u` (undelete), `c` (compress), `S` (synchronous updates),
|
|
`i` (immutable), `a` (append only), `d` (no dump), `A` (no atime updates),
|
|
`E` (encrypted), `I` (indexed directory), `j` (journal data), `t` (no tail-merging),
|
|
`D` (dirsync), `T` (top of directory hierarchy), `e` (extents), `V` (verity),
|
|
`C` (no copy-on-write), `x` (DAX), `N` (inline data), `P` (project hierarchy),
|
|
`F` (case-insensitive directory).
|
|
|
|
On Windows the `extattrs` field returns a string of letters for each set NTFS attribute
|
|
(letters follow the `attrib` command where applicable):
|
|
`R` (read-only), `H` (hidden), `S` (system), `A` (archive), `T` (temporary),
|
|
`P` (sparse file), `L` (reparse point), `C` (compressed), `O` (offline),
|
|
`I` (not content indexed), `E` (encrypted), `V` (integrity stream).
|
|
Note that the Windows letters are case-sensitive (all upper-case).
|
|
|
|
The `has_extattrs` field returns true when any of these attributes are set.
|
|
Use the `has_extattr()` function to check for a specific flag:
|
|
|
|
fselect name from / where has_extattrs = true
|
|
fselect "name, extattrs from /data where has_extattrs = true"
|
|
fselect "name from / where has_extattr('i')"
|
|
fselect "name, extattrs from C:\data where has_extattr('H')"
|
|
|
|
#### String functions
|
|
|
|
Used mostly for formatting results.
|
|
|
|
| Function | Meaning | Example |
|
|
|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|
|
|
| LENGTH or LEN | Length of string value | `select length(name) from /home/user/Downloads order by 1 desc limit 10` |
|
|
| LOWER or LOWERCASE or LCASE | Convert value to lowercase | `select lower(name) from /home/user/Downloads` |
|
|
| UPPER or UPPERCASE or UCASE | Convert value to uppercase | `select upper(name) from /home/user/Downloads` |
|
|
| INITCAP | Returns first letter of each word uppercase, all other letters lowercase | `select initcap('MICHAEL SMITH')` |
|
|
| TO_BASE64 or BASE64 | Encode value to Base64 | `select base64(name) from /home/user/Downloads` |
|
|
| FROM_BASE64 | Decode value from Base64 | `select from_base64('ZnNlbGVjdCByb2Nrcw==')` |
|
|
| CONCAT | Returns concatenated string of expression values | `select CONCAT('Name is ', name, ' size is ', fsize, '!!!') from /home/user/Downloads` |
|
|
| CONCAT_WS | Returns concatenated string of expression values with specified delimiter | `select name, fsize, CONCAT_WS('x', width, height) from /home/user/Images` |
|
|
| LOCATE or POSITION (str, substr, pos) | Returns position of `substr` in `str` value (optionally starting from `pos`) | `select locate('foo', 'barfoo')` |
|
|
| SUBSTRING or SUBSTR (str, pos, len) | Part of `str` value starting from `pos` of (optionally) `len` characters long. Negative `pos` means starting `pos` characters from the end of the string. | `select substr(name, 1, 8) from /home/user/Downloads` |
|
|
| REPLACE (str, from, to) | Replace all occurrences of `from` by `to` | `select replace(name, metallica, MetaLLicA) from /home/user/Music/Rock` |
|
|
| TRIM | Returns string with whitespaces at the beginning and the end stripped | `select trim(title), trim(artist), trim(album) from /home/user/Music into json` |
|
|
| LTRIM | Returns string with whitespaces at the beginning stripped | `select ltrim(title) from /home/user/Music into json` |
|
|
| RTRIM | Returns string with whitespaces at the end stripped | `select rtrim(title) from /home/user/Music into json` |
|
|
|
|
#### Japanese string functions
|
|
|
|
Used for detecting Japanese symbols in file names and such.
|
|
|
|
| Function | Meaning | Example |
|
|
|-------------------------------|-------------------------------------------------|------------------------------------------------------------|
|
|
| CONTAINS_JAPANESE or JAPANESE | Check if string value contains Japanese symbols | `select japanese(name) from /home/user/Downloads` |
|
|
| CONTAINS_KANA or KANA | Check if string value contains kana symbols | `select kana(name) from /home/user/Downloads` |
|
|
| CONTAINS_HIRAGANA or HIRAGANA | Check if string value contains hiragana symbols | `select contains_hiragana(name) from /home/user/Downloads` |
|
|
| CONTAINS_KATAKANA or KATAKANA | Check if string value contains katakana symbols | `select katakana(name) from /home/user/Downloads` |
|
|
| CONTAINS_KANJI or KANJI | Check if string value contains kanji symbols | `select kanji(name) from /home/user/Downloads` |
|
|
|
|
#### Greek string functions
|
|
|
|
Used for detecting Greek symbols in file names and such.
|
|
|
|
| Function | Meaning | Example |
|
|
|---------------------------|-----------------------------------------------|---------------------------------------------------|
|
|
| CONTAINS_GREEK or GREEK | Check if string value contains Greek symbols | `select greek(name) from /home/user/Downloads` |
|
|
|
|
#### Other functions
|
|
|
|
| Function | Meaning | Example |
|
|
|----------------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
|
|
| BIN | Convert integer value to binary representation | `select name, size, bin(size) from /home/user/Downloads` |
|
|
| HEX | Convert integer value to hexadecimal representation | `select name, size, hex(size), upper(hex(size)) from /home/user/Downloads` |
|
|
| OCT | Convert integer value to octal representation | `select name, size, oct(size) from /home/user/Downloads` |
|
|
| ABS | Returns absolute value of the expression | `select abs(-5)` |
|
|
| POWER or POW | Raise the value to the specified power | `select pow(2, 3)` |
|
|
| SQRT | Returns square root of the value | `select sqrt(25)` |
|
|
| LOG | Returns logarithm of the value | `select log(1000)` |
|
|
| LN | Returns natural logarithm of the value | `select ln(10)` |
|
|
| EXP | Returns Euler's number raised to the power of the value | `select exp(2)` |
|
|
| LEAST | Returns the smallest of the expression values | `select least(1, 2, 3)` |
|
|
| GREATEST | Returns the largest of the expression values | `select greatest(1, 2, 3)` |
|
|
| PI | Returns `pi` (π) constant | `select pi()` |
|
|
| FLOOR | Returns the largest integer less than or equal to the value | `select floor(2.5)` |
|
|
| CEIL or CEILING | Returns the smallest integer greater than or equal to the value | `select ceil(2.5)` |
|
|
| ROUND | Returns the value rounded to the nearest integer, or to a given number of decimal places | `select round(2.5)` or `select round(pi(), 2)` |
|
|
| CONTAINS | `true` if file contains string, `false` if not | `select contains(TODO) from /home/user/Projects/foo/src` |
|
|
| COALESCE | Returns first nonempty expression value | `select name, size, COALESCE(sha256, '---') from /home/user/Downloads` |
|
|
| RANDOM or RAND | Returns random integer (from zero to max int, from zero to *arg*, or from *arg1* to *arg2*) | `select path from /home/user/Music order by RAND()` |
|
|
| FORMAT_TIME or PRETTY_TIME | Returns human-readable durations of time in seconds like *2min 26s* | `select format_time(duration) from /home/user/Music` |
|
|
| FORMAT_SIZE | Returns formatted size of a file | `select name, FORMAT_SIZE(size, '%.0') from /home/user/Downloads order by size desc limit 10` |
|
|
|
|
Let's try `FORMAT_SIZE` with different format specifiers:
|
|
|
|
| Specifier | Meaning | Output |
|
|
|-----------------------------------|--------------------------------------------------------------------------------|-------------|
|
|
| `format_size(1678123)` | Default output | 1.60MiB |
|
|
| `format_size(1678123, ' ')` | Put a space before units | 1.60 MiB |
|
|
| `format_size(1678123, '%.0')` | Round up decimal part | 2MiB |
|
|
| `format_size(1678123, '%.1')` | One place for decimal part | 1.6MiB |
|
|
| `format_size(1678123, '%.2')` | Two places for decimal part | 1.60MiB |
|
|
| `format_size(1678123, '%.2 ')` | Two places for decimal part, and put a space before units | 1.60 MiB |
|
|
| `format_size(1678123, '%.2 d')` | Use decimal divider, e.g. 1000-based units, not 1024-based | 1.68 MB |
|
|
| `format_size(1678123, '%.2 c')` | Use conventional format, e.g. 1024-based divider, but display 1000-based units | 1.60 MB |
|
|
| `format_size(1678123, '%.2 k')` | Display file size in specified unit, this time in kibibytes | 1638.79 KiB |
|
|
| `format_size(1678123, '%.2 ck')` | What is a kibibyte? Gimme conventional unit! | 1638.79 KB |
|
|
| `format_size(1678123, '%.0 ck')` | And drop this decimal part! | 1639 KB |
|
|
| `format_size(1678123, '%.0 kb')` | Use 1000-based kilobyte | 1678 KB |
|
|
| `format_size(1678123, '%.0kb')` | Don't put a space | 1678KB |
|
|
| `format_size(1678123, '%.0s')` | Use short units | 2M |
|
|
| `format_size(1678123, '%.0 s')` | Use short units with a space | 2 M |
|
|
|
|
### File size units
|
|
|
|
| Specifier | Meaning | Bytes |
|
|
|--------------|----------|---------------------------|
|
|
| `t` or `tib` | tebibyte | 1024 * 1024 * 1024 * 1024 |
|
|
| `tb` | terabyte | 1000 * 1000 * 1000 * 1000 |
|
|
| `g` or `gib` | gibibyte | 1024 * 1024 * 1024 |
|
|
| `gb` | gigabyte | 1000 * 1000 * 1000 |
|
|
| `m` or `mib` | mebibyte | 1024 * 1024 |
|
|
| `mb` | megabyte | 1000 * 1000 |
|
|
| `k` or `kib` | kibibyte | 1024 |
|
|
| `kb` | kilobyte | 1000 |
|
|
|
|
fselect size, path from /home/user/tmp where size gt 2g
|
|
fselect fsize, path from /home/user/tmp where size = 5mib
|
|
fselect hsize, path from /home/user/tmp where size lt 8kb
|
|
|
|
### Search roots
|
|
|
|
path [option N] [option] [option] [option...][, path2 [option...]]
|
|
|
|
When you put a directory to search at, you can specify some options.
|
|
|
|
| Option | Meaning |
|
|
|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
| mindepth N | Minimum search depth. Default is unlimited. Depth 1 means skip one directory level and search further. |
|
|
| maxdepth N | Maximum search depth. Default is unlimited. Depth 1 means search the mentioned directory only. Depth 2 means search mentioned directory and its subdirectories. Synonym is `depth`. |
|
|
| symlinks | If specified, search process will follow symlinks. Default is not to follow. Synonym is `sym`. |
|
|
| archives | Search within archives. Only zip archives are supported. Default is not to include archived content into the search results. Synonym is `arc`. |
|
|
| gitignore | Search respects `.gitignore` files found. Synonym is `git`. |
|
|
| hgignore | Search respects `.hgignore` files found. Synonym is `hg`. |
|
|
| dockerignore | Search respects `.dockerignore` files found. Synonym is `dock`. |
|
|
| nogitignore | Disable `.gitignore` parsing during the search. Synonym is `nogit`. |
|
|
| nohgignore | Disable `.hgignore` parsing during the search. Synonym is `nohg`. |
|
|
| nodockerignore | Disable `.dockerignore` parsing during the search. Synonym is `nodock`. |
|
|
| dfs | Depth-first search mode. |
|
|
| bfs | Breadth-first search mode. This is the default. |
|
|
| regexp | Use regular expressions to search within multiple roots. Synonym is `rx`. |
|
|
|
|
### Operators
|
|
|
|
* `=` or `==` or `eq`
|
|
* `!=` or `<>` or `ne`
|
|
* `===` or `eeq`
|
|
* `!==` or `ene`
|
|
* `>` or `gt`
|
|
* `>=` or `gte` or `ge`
|
|
* `<` or `lt`
|
|
* `<=` or `lte` or `le`
|
|
* `=~` or `~=` or `regexp` or `rx`
|
|
* `!=~` or `!~=` or `notrx`
|
|
* `like`
|
|
* `notlike`
|
|
* `between`
|
|
* `in`
|
|
* `exists`
|
|
|
|
### Arithmetic operators
|
|
|
|
| Operator | Alias |
|
|
|----------|--------|
|
|
| + | plus |
|
|
| - | minus |
|
|
| * | mul |
|
|
| / | div |
|
|
| % | mod |
|
|
|
|
### Subqueries in the `FROM` clause
|
|
|
|
The `FROM` clause can take a parenthesized inner query in place of (or alongside) a filesystem path.
|
|
The inner query is executed first, and the rows it produces become the input set the outer query
|
|
iterates over — its `WHERE`, `SELECT`, `LIMIT`, and `ORDER BY` clauses then run against that set
|
|
without any further directory traversal.
|
|
|
|
```sql
|
|
select name from (select * from /projects depth 2)
|
|
```
|
|
|
|
You can attach options like an alias to the subselect root just like a regular root:
|
|
|
|
```sql
|
|
select src.name, src.size from (select path from /projects depth 2) as src where src.size > 1024
|
|
```
|
|
|
|
A `WHERE` clause inside the subselect filters the input set; a `WHERE` clause outside filters the
|
|
outer rows produced from that input set:
|
|
|
|
```sql
|
|
select name from (select path from /projects depth 2 where size > 100) where name like '%.rs'
|
|
```
|
|
|
|
Subselects may be nested.
|
|
|
|
### Subqueries for `IN` and `EXISTS`
|
|
|
|
Subqueries in **fselect** allow you to nest queries within queries, enabling powerful file search operations that compare results across different directory trees.
|
|
Subqueries can be used with the `IN`, `NOT IN`, `EXISTS`, and `NOT EXISTS` operators to create sophisticated filtering logic.
|
|
|
|
**Important:** When using subqueries that need to reference the parent query's results, you must bind search roots using aliases with the `AS` keyword.
|
|
This creates a correlated subquery where the inner query can reference columns from the outer query.
|
|
|
|
> [!CAUTION]
|
|
> This feature is still in development. Random queries can fail for no obvious reason.
|
|
|
|
#### General Subquery Syntax
|
|
```sql
|
|
SELECT columns FROM root AS alias
|
|
WHERE column operator (SELECT columns2 FROM root2 AS alias2 WHERE condition)
|
|
```
|
|
|
|
#### Supported Operators
|
|
- `IN` - Tests if a value exists in the subquery result set
|
|
- `NOT IN` - Tests if a value does not exist in the subquery result set
|
|
- `EXISTS` - Tests if the subquery returns any rows
|
|
- `NOT EXISTS` - Tests if the subquery returns no rows
|
|
|
|
#### `IN` Operator
|
|
|
|
The `IN` operator checks if a value from the outer query matches any value returned by the subquery.
|
|
|
|
**Example:** Find files in `/backup` that have the same size as files in `/production`:
|
|
|
|
```sql
|
|
select name, size from /backup
|
|
where size in (select size from /production)
|
|
```
|
|
|
|
**Example:** Find files with multiple levels of correlation:
|
|
|
|
```sql
|
|
select 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
|
|
)
|
|
)
|
|
)
|
|
```
|
|
|
|
This query finds files in `/test1` where:
|
|
1. Size is greater than 100 bytes
|
|
2. Size matches files in `/test2`
|
|
3. Those `/test2` files have names matching files in `/test3`
|
|
4. Those `/test3` files have modification times matching files in `/test4` (smaller than 200 bytes)
|
|
|
|
**Example:** Find files in `/home/user/docs` where the filename appears in subdirectories with the same extension:
|
|
|
|
```sql
|
|
select name, path from /home/user/docs as parent
|
|
where name in (
|
|
select name from /home/user/docs/archive as child
|
|
where child.ext = parent.ext
|
|
)
|
|
```
|
|
|
|
The `as parent` and `as child` aliases allow the subquery to reference the outer query's `ext` column.
|
|
|
|
#### `NOT IN` Operator
|
|
|
|
The `NOT IN` operator checks if a value from the outer query does NOT match any value in the subquery.
|
|
|
|
**Example:** Find files in `/current` that don't exist in `/backup` (by name):
|
|
|
|
```sql
|
|
select name, path from /current
|
|
where name not in (select name from /backup)
|
|
```
|
|
|
|
**Example:** Find config files that exist in production but not in staging:
|
|
|
|
```sql
|
|
select name, path from /production/config as prod
|
|
where name not in (
|
|
select name from /staging/config where ext = 'cfg'
|
|
)
|
|
```
|
|
|
|
**Example:** Find files unique to a directory by both name and size:
|
|
|
|
```sql
|
|
select name, size, path from /home/user/projects as proj
|
|
where name not in (
|
|
select name from /home/user/archive as arch
|
|
where arch.size = proj.size
|
|
)
|
|
```
|
|
|
|
**Important Note:** `NOT IN` can produce unexpected results if the subquery returns any NULL/empty values.
|
|
When in doubt, use `NOT EXISTS` instead (see below).
|
|
|
|
#### `EXISTS` Operator
|
|
|
|
The `EXISTS` operator returns true if the subquery returns at least one row.
|
|
It's often more efficient than `IN` and handles NULL/empty values better.
|
|
|
|
**Example:** Find directories that contain image files:
|
|
|
|
```sql
|
|
select path from /home/user as parent
|
|
where is_dir and exists (
|
|
select * from /home/user as child
|
|
where child.dir = parent.path and child.is_image
|
|
)
|
|
```
|
|
|
|
**Example:** Find files in `/data` that have a backup in `/backup` with the same name:
|
|
|
|
```sql
|
|
select name, path, size from /data as data
|
|
where exists (
|
|
select * from /backup as backup
|
|
where backup.name = data.name
|
|
)
|
|
```
|
|
|
|
**Example:** Find directories that have been modified recently (contain files modified in the last 7 days):
|
|
|
|
```sql
|
|
select path from /home/user/projects gitignore as proj
|
|
where is_dir
|
|
and exists (
|
|
select * from /home/user/projects as files
|
|
where is_file
|
|
and files.dir = proj.abspath
|
|
and files.modified >= 'last week'
|
|
)
|
|
```
|
|
|
|
#### `NOT EXISTS` Operator
|
|
|
|
The `NOT EXISTS` operator returns true if the subquery returns zero rows. This is the safest way to check for non-matching data.
|
|
|
|
**Example:** Find files in `/production` that don't have a backup:
|
|
|
|
```sql
|
|
select name, path from /production as prod
|
|
where not exists (
|
|
select * from /backup as backup
|
|
where backup.name = prod.name
|
|
)
|
|
```
|
|
|
|
**Example:** Find files in `/cache` that don't have corresponding source files:
|
|
|
|
```sql
|
|
select name, path from /cache as cache
|
|
where not exists (
|
|
select * from /source as source
|
|
where source.name = cache.name
|
|
and source.size > 0
|
|
)
|
|
```
|
|
|
|
**Example:** Find source files that have no corresponding test files:
|
|
|
|
```sql
|
|
select name, path from /home/user/src as src
|
|
where ext = 'rs' and not exists (
|
|
select * from /home/user/tests as tests
|
|
where tests.name like concat(src.name, '_test%')
|
|
and tests.ext = 'rs'
|
|
)
|
|
```
|
|
|
|
### Unix timestamps
|
|
|
|
The `atime`, `mtime`, and `ctime` fields return raw Unix timestamps (seconds since epoch) as integers.
|
|
These are available only on Unix platforms and are useful when you need numeric comparison
|
|
or want to pass exact values to external tools.
|
|
|
|
fselect name, mtime from /home/user/projects
|
|
fselect name, atime from /home/user where atime gt 1700000000
|
|
fselect "name, format_time(mtime) from /home/user"
|
|
fselect "name, day(accessed), mtime from /home/user"
|
|
|
|
Unlike `accessed`, `modified`, and `created` which return formatted date/time strings,
|
|
these fields return the raw integer value from the filesystem metadata.
|
|
|
|
The companion `atime_nsec`, `mtime_nsec`, and `ctime_nsec` fields return the sub-second
|
|
nanosecond component of the respective timestamp, for cases where second-level precision
|
|
is not enough.
|
|
|
|
fselect name, mtime, mtime_nsec from /home/user/projects
|
|
|
|
### Git fields
|
|
|
|
The git fields look up the repository containing each file (the nearest enclosing work tree)
|
|
and report per-file information. `git_status` returns one of `clean`, `modified`, `staged`,
|
|
`untracked`, `conflicted`, or `ignored`. The `git_last_commit_*` fields walk the repository
|
|
history to find the last commit that touched the file, like `git log -1 -- path`, so they are
|
|
relatively expensive on large histories.
|
|
|
|
fselect name, git_status from /home/user/projects/repo where git_status = modified
|
|
fselect path from /home/user/projects/repo where is_git_tracked = false and is_gitignored = false
|
|
fselect name, git_last_commit_date, git_last_commit_author from src
|
|
fselect path, git_branch from /home/user/projects where is_git_repo = true depth 2
|
|
|
|
`is_git_repo` simply checks for a `.git` subdirectory (or file) and is available in every build.
|
|
All other git fields require the `git` feature (enabled by default).
|
|
|
|
### Date and time specifiers
|
|
|
|
When you specify inexact date and time with `=` or `!=` operator, **fselect** understands it as an interval.
|
|
|
|
fselect path from /home/user where modified = 2017-05-01
|
|
|
|
`2017-05-01` means all day long from 00:00:00 to 23:59:59.
|
|
|
|
fselect path from /home/user where modified = '2017-05-01 15'
|
|
|
|
`2017-05-01 15` means one hour from 15:00:00 to 15:59:59.
|
|
|
|
fselect path from /home/user where modified ne '2017-05-01 15:10'
|
|
|
|
`2017-05-01 15:10` is a 1-minute interval from 15:10:00 to 15:10:59.
|
|
|
|
Other operators assume the exact date and time, which could be specified in a freer way:
|
|
|
|
fselect "path from /home/user where modified === 'apr 1'"
|
|
fselect "path from /home/user where modified gte 'last fri'"
|
|
fselect path from /home/user where modified gte '01/05'
|
|
|
|
Or simply use relative offsets as days:
|
|
|
|
fselect created, path from /home/user where created gte -2
|
|
|
|
[More about writing dates in plain English](https://github.com/stevedonovan/chrono-english)
|
|
|
|
**fselect** uses *UK* locale by default, not American style dates, i.e. `08/02` means *February 8th* by default.
|
|
|
|
To change this behavior, supply `--us-dates` option to the `fselect` command, or put `us_dates = true` into the configuration file.
|
|
|
|
The safest way to specify dates is to use ISO 8601 format: `YYYY-MM-DD HH:MM:SS`.
|
|
|
|
### Regular expressions
|
|
|
|
[Rust flavor regular expressions](https://docs.rs/regex/latest/regex/index.html#syntax) are used.
|
|
|
|
### MIME and file types
|
|
|
|
For MIME guessing use field `mime`. It returns a simple string with a deduced MIME type,
|
|
which is not always accurate.
|
|
|
|
fselect path, mime, is_binary, is_text from /home/user
|
|
|
|
`is_binary` and `is_text` return `true` or `false` based on MIME type detected.
|
|
Once again, this should not be considered as a 100% accurate result,
|
|
or even possible at all to detect a correct file type.
|
|
|
|
Other fields listed below **do NOT** use MIME detection.
|
|
Assumptions are being made based on file extension.
|
|
|
|
The lists below could be edited with the configuration file.
|
|
|
|
| Search field | Extensions |
|
|
|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
| <a name="ext_archive"></a> `is_archive` | .7z, .bz2, .bzip2, .gz, .gzip, .lz, .rar, .tar, .xz, .zip |
|
|
| <a name="ext_audio"></a> `is_audio` | .aac, .aiff, .amr, .flac, .gsm, .m4a, .m4b, .m4p, .mp3, .ogg, .wav, .wma |
|
|
| <a name="ext_book"></a> `is_book` | .azw3, .chm, .djv, .djvu, .epub, .fb2, .mobi, .pdf |
|
|
| <a name="ext_doc"></a> `is_doc` | .accdb, .doc, .docm, .docx, .dot, .dotm, .dotx, .mdb, .odp, .ods, .odt, .pdf, .potm, .potx, .ppt, .pptm, .pptx, .rtf, .xlm, .xls, .xlsm, .xlsx, .xlt, .xltm, .xltx, .xps |
|
|
| <a name="ext_font"></a> `is_font` | .eot, .fon, .otc, .otf, .ttc, .ttf, .woff, .woff2 |
|
|
| <a name="ext_image"></a> `is_image` | .bmp, .exr, .gif, .heic, .jpeg, .jpg, .jxl, .png, .svg, .tga, .tiff, .webp |
|
|
| <a name="ext_source"></a> `is_source` | .asm, .awk, .bas, .c, .cc, .ceylon, .clj, .coffee, .cpp, .cs, .d, .dart, .elm, .erl, .go, .groovy, .h, .hh, .hpp, .java, .jl, .js, .jsp, .jsx, .kt, .kts, .lua, .nim, .pas, .php, .pl, .pm, .py, .qml, .rb, .rs, .scala, .sol, .swift, .tcl, .ts, .vala, .vb, .zig |
|
|
| <a name="ext_video"></a> `is_video` | .3gp, .avi, .flv, .m4p, .m4v, .mkv, .mov, .mp4, .mpeg, .mpg, .webm, .wmv |
|
|
|
|
fselect is_archive, path from /home/user
|
|
fselect is_audio, is_video, path from /home/user/multimedia
|
|
fselect path from /home/user where is_doc != 1
|
|
fselect path from /home/user where is_image = false
|
|
fselect path from /home/user where is_video != true
|
|
|
|
### Audio support
|
|
|
|
**fselect** reads audio metadata via [lofty](https://crates.io/crates/lofty), so it can search by
|
|
bitrate, sampling frequency, title, artist, album, genre, year, comment, track, and disc number across
|
|
many formats: MP3, FLAC, Ogg Vorbis, Opus, M4A/AAC/ALAC, WAV, AIFF, APE, WavPack, Musepack, and Speex.
|
|
|
|
Duration is measured in seconds.
|
|
|
|
fselect duration, bitrate, path from /home/user/music
|
|
fselect mp3_year, album, title from /home/user/music where artist like %Vampire% and bitrate gte 320
|
|
fselect bitrate, freq, path from /home/user/music where genre = Rap or genre = HipHop
|
|
fselect path, title, artist from /home/user/music where ext in (flac, opus, m4a)
|
|
|
|
### File hashes
|
|
|
|
| Column | Meaning |
|
|
|------------------------|---------------------------|
|
|
| `sha1` | SHA-1 digest of a file |
|
|
| `sha2_256` or `sha256` | SHA2-256 digest of a file |
|
|
| `sha2_512` or `sha512` | SHA2-512 digest of a file |
|
|
| `sha3_512` or `sha3` | SHA3-512 digest of a file |
|
|
|
|
fselect path, sha256, 256 from /home/user/archive limit 5
|
|
fselect path from /home/user/Download where sha1 like cb23ef45%
|
|
|
|
### Output formats
|
|
|
|
... into FORMAT
|
|
|
|
| Format | Description |
|
|
|----------|---------------------------------------------------------------------------------|
|
|
| `tabs` | default, columns are separated with tabulation |
|
|
| `lines` | each column goes at a separate line |
|
|
| `list` | columns are separated with NULL symbol, similar to `-print0` argument of `find` |
|
|
| `csv` | comma-separated columns |
|
|
| `json` | array of resulting objects with requested columns |
|
|
| `html` | HTML document with table |
|
|
|
|
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
|
|
fselect path from /home/user into list | xargs -0 grep foobar
|
|
|
|
### Configuration file
|
|
|
|
**fselect** tries to create a new configuration file if one doesn't exist.
|
|
|
|
Usual location on Linux:
|
|
|
|
/home/user_name/.config/fselect/config.toml
|
|
|
|
On Windows:
|
|
|
|
C:\Users\user_name\AppData\Roaming\jhspetersson\fselect\config.toml
|
|
|
|
Fresh config is filled with defaults, feel free to update it.
|
|
|
|
If no config on the standard paths is found, **fselect** checks its presence next to the executable.
|
|
You can also specify a config location with a runtime option, e.g.:
|
|
|
|
fselect --config /home/user_name/fselect_custom.toml name, size from /home/user_name/Music where is_audio = 1
|
|
|
|
#### Check for updates
|
|
|
|
**fselect** can be built with `update-notifications` feature, that enables automatic check for updates.
|
|
This check is disabled by default. To enable it, put
|
|
|
|
check_for_updates = true
|
|
|
|
into the config file.
|
|
|
|
### Bash completion
|
|
|
|
**fselect** comes with a bash completion script (`fselect-completion.bash`) that provides tab completion for:
|
|
- Directory paths after the `from` keyword
|
|
- Output formats after the `into` keyword
|
|
- Fields and functions in other contexts
|
|
|
|
To enable bash completion for **fselect**, you need to install the completion script. The installation method varies depending on your Linux distribution:
|
|
|
|
#### Ubuntu/Debian and Red Hat/Fedora/CentOS
|
|
|
|
1. Copy the completion script to the bash completion directory:
|
|
|
|
```bash
|
|
sudo cp fselect-completion.bash /etc/bash_completion.d/fselect
|
|
```
|
|
|
|
2. Make the script executable:
|
|
|
|
```bash
|
|
sudo chmod +x /etc/bash_completion.d/fselect
|
|
```
|
|
|
|
3. Source the script or restart your shell:
|
|
|
|
```bash
|
|
source /etc/bash_completion.d/fselect
|
|
```
|
|
|
|
#### Arch Linux
|
|
|
|
1. Copy the completion script to the bash completion directory:
|
|
|
|
```bash
|
|
sudo cp fselect-completion.bash /usr/share/bash-completion/completions/fselect
|
|
```
|
|
|
|
2. Make the script executable:
|
|
|
|
```bash
|
|
sudo chmod +x /usr/share/bash-completion/completions/fselect
|
|
```
|
|
|
|
3. Source the script or restart your shell:
|
|
|
|
```bash
|
|
source /usr/share/bash-completion/completions/fselect
|
|
```
|
|
|
|
#### Manual installation (any Linux distribution)
|
|
|
|
If your distribution doesn't have a standard location for bash completion scripts, or if you don't have root access, you can install the script in your home directory:
|
|
|
|
1. Create a directory for bash completion scripts if it doesn't exist:
|
|
|
|
```bash
|
|
mkdir -p ~/.bash_completion.d
|
|
```
|
|
|
|
2. Copy the completion script to this directory:
|
|
|
|
```bash
|
|
cp fselect-completion.bash ~/.bash_completion.d/fselect
|
|
```
|
|
|
|
3. Make the script executable:
|
|
|
|
```bash
|
|
chmod +x ~/.bash_completion.d/fselect
|
|
```
|
|
|
|
4. Add the following line to your `~/.bashrc` file:
|
|
|
|
```bash
|
|
source ~/.bash_completion.d/fselect
|
|
```
|
|
|
|
5. Source your `~/.bashrc` file or restart your shell:
|
|
|
|
```bash
|
|
source ~/.bashrc
|
|
```
|
|
|
|
### Command-line arguments
|
|
|
|
| Argument | Meaning |
|
|
|-------------------------------------------|----------------------------------------------|
|
|
| `--interactive` or `-i` or `/i` | Run in [interactive mode](#interactive-mode) |
|
|
| `--config` or `-c` or `/config` | Specify config file location |
|
|
| `--nocolor` or `--no-color` or `/nocolor` | Disable colors |
|
|
| `--no-errors` | Suppress error reporting |
|
|
| `--everything` | Use the *Everything* index as the file source (Windows, requires the `everything` build feature) |
|
|
| `--plocate` | Use the *plocate* index as the file source (Linux, requires the `plocate` build feature) |
|
|
| `--help` or `-h` or `/?` or `/h` | Show help and exit |
|
|
|
|
### Index-backed search (Everything / plocate)
|
|
|
|
**fselect** can optionally use an external file-name index as the source of candidate paths instead
|
|
of walking the filesystem. Because these indexes are prebuilt, enumerating a large directory tree is
|
|
typically much faster. Two backends are supported, each behind an opt-in build feature:
|
|
|
|
- **Everything** (Windows) — the [voidtools *Everything*](https://www.voidtools.com/) engine, via its
|
|
client DLL. Enable with the `everything` feature and the `--everything` flag.
|
|
- **plocate** (Linux) — the [`plocate`](https://plocate.sesse.net/) `locate` replacement, invoked as a
|
|
subprocess. Enable with the `plocate` feature and the `--plocate` flag.
|
|
|
|
```
|
|
# Windows
|
|
cargo build --release --features everything
|
|
fselect --everything "name, size from C:\Users where size gt 100mb"
|
|
|
|
# Linux
|
|
cargo build --release --features plocate
|
|
fselect --plocate "name, size from /home where size gt 100mb"
|
|
```
|
|
|
|
Both backends behave the same way:
|
|
|
|
- They can also be enabled via the configuration file (`everything = true` / `plocate = true`).
|
|
- If the backend is unavailable — *Everything* not running / DLL missing, or the `plocate` binary or
|
|
its database missing — **fselect** transparently falls back to normal traversal.
|
|
- `mindepth`/`maxdepth` (and `depth`) constraints are applied to the index results.
|
|
- The `where`/`order by`/`select` logic, functions, and all fields work exactly as with traversal —
|
|
the index only supplies the candidate paths.
|
|
- Options that require reading the filesystem structure — searching `archives`, or applying
|
|
`.gitignore`/`.hgignore`/`.dockerignore` filters — automatically use normal traversal instead.
|
|
- Locations the index does not cover (for example, some network drives, or a stale `plocate`
|
|
database) will return no results in this mode.
|
|
|
|
### Interactive mode
|
|
|
|
In interactive mode, you can:
|
|
- execute queries directly without calling `fselect` every time
|
|
- use any characters without escaping them from the shell
|
|
- run multiple searches sequentially without restarting the tool
|
|
- edit and refine queries iteratively
|
|
- use command history (up/down arrows) to recall previous queries
|
|
- get current directory with `pwd` and change it with `cd`
|
|
- suppress error reporting with `errors off`
|
|
- exit with `quit`, `exit`, Ctrl+C or Ctrl+D
|
|
|
|
### Environment variables
|
|
|
|
**fselect** respects `NO_COLOR` [environment variable](https://no-color.org).
|
|
|
|
### Exit values
|
|
|
|
| Value | Meaning |
|
|
|-------|---------------------------------------------------------------------|
|
|
| 0 | everything OK |
|
|
| 1 | I/O error has occurred during any directory listing or file reading |
|
|
| 2 | error during parsing or evaluation of the search query |
|