Fuzzy Finder in rust!


Crates.io Build & Test

Life is short, skim!

Half of our life is spent on navigation: files, lines, commands… You need skim! It is a general fuzzy finder that saves you time.

skim demo

skim provides a single executable: sk. Basically anywhere you would want to use grep, try sk instead.

Table of contents


The skim project contains several components:

  1. sk executable -- the core.
  2. sk-tmux -- script for launching sk in a tmux pane.
  3. Vim/Nvim plugin -- to call sk inside Vim/Nvim. check skim.vim for more Vim support.

Package Managers

Distribution Package Manager Command
macOS Homebrew brew install sk
macOS MacPorts sudo port install skim
Fedora dnf dnf install skim
Alpine apk apk add skim
Arch pacman pacman -S skim

Install as Vim plugin

Via vim-plug (recommended):

Plug 'lotabout/skim', { 'dir': '~/.skim', 'do': './install' }

Hard Core

Any of the following applies:

  • Using Git
    $ git clone --depth 1 [email protected]:lotabout/skim.git ~/.skim
    $ ~/.skim/install
  • Using Binary: directly download the sk executable.
  • Install from crates.io: cargo install skim
  • Build Manually
    $ git clone --depth 1 [email protected]:lotabout/skim.git ~/.skim
    $ cd ~/.skim
    $ cargo install
    $ cargo build --release
    $ # put the resulting `target/release/sk` executable on your PATH.


skim can be used as a general filter (like grep) or as an interactive interface for invoking commands.

As filter

Try the following

# directly invoke skim

# or pipe some input to it: (press TAB key select multiple items with -m enabled)
vim $(find . -name "*.rs" | sk -m)

The above command will allow you to select files with ".rs" extension and open the ones you selected in Vim.

As Interactive Interface

skim can invoke other commands dynamically. Normally you would want to integrate it with grep, ack, ag, or rg for searching contents in a project directory:

# works with grep
sk --ansi -i -c 'grep -rI --color=always --line-number "{}" .'
# works with ack
sk --ansi -i -c 'ack --color "{}"'
# works with ag
sk --ansi -i -c 'ag --color "{}"'
# works with rg
sk --ansi -i -c 'rg --color=always --line-number "{}"'

interactive mode demo

Key Bindings

Some commonly used key bindings:

Key Action
Enter Accept (select current one and quit)
ESC/Ctrl-G Abort
Ctrl-P/Up Move cursor up
Ctrl-N/Down Move cursor Down
TAB Toggle selection and move down (with -m)
Shift-TAB Toggle selection and move up (with -m)

For full list of key bindings, check out the man page (man sk).

Search Syntax

skim borrowed fzf's syntax for matching items:

Token Match type Description
text fuzzy-match items that match text
^music prefix-exact-match items that start with music
.mp3$ suffix-exact-match items that end with .mp3
'wild exact-match (quoted) items that include wild
!fire inverse-exact-match items that do not include fire
!.mp3$ inverse-suffix-exact-match items that do not end with .mp3

skim also supports the combination of tokens.

  • Whitespace has the meaning of AND. With the term src main, skim will search for items that match both src and main.
  • | means OR (note the spaces around |). With the term .md$ | .markdown$, skim will search for items ends with either .md or .markdown.
  • OR has higher precedence. So readme .md$ | .markdown$ is grouped into readme AND (.md$ OR .markdown$).

In case that you want to use regular expressions, skim provides regex mode:

sk --regex

You can switch to regex mode dynamically by pressing Ctrl-R (Rotate Mode).

exit code

Exit Code Meaning
0 Exit normally
1 No Match found
130 Abort by Ctrl-C/Ctrl-G/ESC/etc...


The doc here is only a preview, please check the man page (man sk) for a full list of options.


Specify the bindings with comma separated pairs (no space allowed), example:

sk --bind 'alt-a:select-all,alt-d:deselect-all'

Additionally, use + to concatenate actions, such as execute-silent(echo {} | pbcopy)+abort.

See the KEY BINDINGS section of the man page for details.

Sort Criteria

There are five sort keys for results: score, index, begin, end, length, you can specify how the records are sorted by sk --tiebreak score,index,-begin or any other order you want.

Color Scheme

It is a high chance that you are a better artist than me. Luckily you won't be stuck with the default colors, skim supports customization of the color scheme.


The configuration of colors starts with the name of the base color scheme, followed by custom color mappings. For example:

sk --color=current_bg:24
sk --color=light,fg:232,bg:255,current_bg:116,info:27

See --color option in the man page for details.


  • --ansi: to parse ANSI color codes (e.g., \e[32mABC) of the data source
  • --regex: use the query as regular expression to match the data source

Advanced Topics

Interactive mode

With "interactive mode", you could invoke command dynamically. Try out:

sk --ansi -i -c 'rg --color=always --line-number "{}"'

How it works?

skim's interactive mode

  • Skim could accept two kinds of source: command output or piped input
  • Skim has two kinds of prompts: A query prompt to specify the query pattern and a command prompt to specify the "arguments" of the command
  • -c is used to specify the command to execute while defaults to SKIM_DEFAULT_COMMAND
  • -i is to tell skim open command prompt on startup, which will show c> by default.

If you want to further narrow down the results returned by the command, press Ctrl-Q to toggle interactive mode.

Executing external programs

You can set up key bindings for starting external processes without leaving skim (execute, execute-silent).

# Press F1 to open the file with less without leaving skim
# Press CTRL-Y to copy the line to clipboard and aborts skim (requires pbcopy)
sk --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort'

Preview Window

This is a great feature of fzf that skim borrows. For example, we use 'ag' to find the matched lines, once we narrow down to the target lines, we want to finally decide which lines to pick by checking the context around the line. grep and ag has an option --context, skim can do better with preview window. For example:

sk --ansi -i -c 'ag --color "{}"' --preview "preview.sh {}"

(Note the preview.sh is a script to print the context given filename:lines:columns) You got things like this:

preview demo

How does it work?

If the preview command is given by the --preview option, skim will replace the {} with the current highlighted line surrounded by single quotes, call the command to get the output, and print the output on the preview window.

Sometimes you don't need the whole line for invoking the command. In this case you can use {}, {1..}, {..3} or {1..5} to select the fields. The syntax is explained in the section "Fields Support".

Last, you might want to configure the position of preview windows, use --preview-window.

  • --preview-window up:30% to put the window in the up position with height 30% of the total height of skim.
  • --preview-window left:10:wrap, to specify the wrap allows the preview window to wrap the output of the preview command.
  • --preview-window wrap:hidden to hide the preview window at startup, later it can be shown by the action toggle-preview.

Fields support

Normally only plugin users need to understand this.

For example, you have the data source with the format:

<filename>:<line number>:<column number>

However, you want to search <filename> only when typing in queries. That means when you type 21, you want to find a <filename> that contains 21, but not matching line number or column number.

You can use sk --delimiter ':' --nth 1 to achieve this.

Also you can use --with-nth to re-arrange the order of fields.

Range Syntax

  • <num> -- to specify the num-th fields, starting with 1.
  • start.. -- starting from the start-th fields, and the rest.
  • ..end -- starting from the 0-th field, all the way to end-th field, including end.
  • start..end -- starting from start-th field, all the way to end-th field, including end.

Use as a library

Skim can be used as a library in your Rust crates.

First, add skim into your Cargo.toml:

skim = "*"

Then try to run this simple example:

extern crate skim;
use skim::prelude::*;
use std::io::Cursor;

pub fn main() {
    let options = SkimOptionsBuilder::default()

    let input = "aaaaa\nbbbb\nccc".to_string();

    // `SkimItemReader` is a helper to turn any `BufRead` into a stream of `SkimItem`
    // `SkimItem` was implemented for `AsRef<str>` by default
    let item_reader = SkimItemReader::default();
    let items = item_reader.of_bufread(Cursor::new(input));

    // `run_with` would read and show items from the stream
    let selected_items = Skim::run_with(&options, Some(items))
        .map(|out| out.selected_items)
        .unwrap_or_else(|| Vec::new());

    for item in selected_items.iter() {
        print!("{}{}", item.output(), "\n");

Given an Option<SkimItemReceiver>, skim will read items accordingly, do its job and bring us back the user selection including the selected items, the query, etc. Note that:

  • SkimItemReceiver is crossbeam::channel::Receiver<Arc<dyn SkimItem>>
  • If it is none, it will invoke the given command and read items from command output
  • Otherwise, it will read the items from the (crossbeam) channel.

Trait SkimItem is provided to customize how a line could be displayed, compared and previewed. It is implemented by default for AsRef<str>

Plus, SkimItemReader is a helper to convert a BufRead into SkimItemReceiver (we can easily turn a File for String into BufRead). So that you could deal with strings or files easily.

Check more examples under examples/ directory.


How to ignore files?

Skim invokes find . to fetch a list of files for filtering. You can override that by setting the environment variable SKIM_DEFAULT_COMMAND. For example:

$ SKIM_DEFAULT_COMMAND="fd --type f || git ls-tree -r --name-only HEAD || rg --files || find ."
$ sk

You could put it in your .bashrc or .zshrc if you like it to be default.

Some files are not shown in Vim plugin

If you use the Vim plugin and execute the :SK command, you might find some of your files not shown.

As described in #3, in the Vim plugin, SKIM_DEFAULT_COMMAND is set to the command by default:

let $SKIM_DEFAULT_COMMAND = "git ls-tree -r --name-only HEAD || rg --files || ag -l -g \"\" || find ."

That means, the files not recognized by git will not shown. Either override the default with let $SKIM_DEFAULT_COMMAND = '' or find the missing file by yourself.

Differences to fzf

fzf is a command-line fuzzy finder written in Go and skim tries to implement a new one in Rust!

This project is written from scratch. Some decisions of implementation are different from fzf. For example:

  1. skim is a binary as well as a library while fzf is only a binary.
  2. skim has an interactive mode.
  3. skim supports pre-selection
  4. The fuzzy search algorithm is different.
  5. UI of showing matched items. fzf will show only the range matched while skim will show each character matched. (fzf has this now)
  6. skim's range syntax is Git style: now it is the same with fzf.

How to contribute

Create new issues if you meet any bugs or have any ideas. Pull requests are warmly welcomed.

  • `sk` suddenly stop working (no response)

    `sk` suddenly stop working (no response)

    Hi, I am a big fan of Rust and has since moved to sk from fzf. Thanks for the great work.

    Before updating 0.8.1, sk was working fine at all. After updating, all of a sudden it just stops working. sk does not have any response. I use fzf side-by-side with sk and fzf is still working as normal.

    Using pacman. I removed skim. Reinstall it. Issue persists.

    # Start cleanly
    bash --noprofile --norc
    # working normally
    # no response
    # no response
    # no response
    echo $?
    # 101
    sk --version
    # 0.8.1
    sk --help
    # Usage: sk [options]
    #   Options
    #     -h, --help           print this help menu
    #     --version [...]
    # [truncated ..............................................]
    cat /etc/os-release
    #    1 NAME="Arch Linux"
    #    2 PRETTY_NAME="Arch Linux"
    #    3 ID=arch
    #    4 BUILD_ID=rolling
    #    5 ANSI_COLOR="0;36"
    #    6 HOME_URL="https://www.archlinux.org/"
    #    7 DOCUMENTATION_URL="https://wiki.archlinux.org/"
    #    8 SUPPORT_URL="https://bbs.archlinux.org/"
    #    9 BUG_REPORT_URL="https://bugs.archlinux.org/"
    #   10 LOGO=archlinux
    opened by JodyStats 23
  • 'abort' doesn't abort all the time

    'abort' doesn't abort all the time

    When running find|sk --bind 'enter:execute(echo {})+abort', skim doesn't always abort.

    That is, the selection is printed, then a newline. But the caret stays on an empty line below, skim keeps running, and hitting enter again doesn't get us back to the shell. Typing ctrl-c is needed to end the program.

    Tested on Ubuntu 19.04 running konsole, bash 5.0.3 and skim 0.7.0.

    opened by ngirard 12
  • Reuse as a library?

    Reuse as a library?

    I'd like to reuse skim inside my own application. With this, I mean reusing skim as a library. Inside my code, I'd like to pass Skim my sources, hand over control to skim which runs its CLI and once the user selects something, returns the selected items back to me. Mainly these are the features I'd like to have:

    • fuzzy finding, including interactivity and options (multi, non-multi).
    • providing my own input source, while decoupling the text that is shown to the user from what I get returned.

    A possible API could be this:

    /// Defined by skim.
    pub trait SkimItem: Send {
       /// The text displayed to the user.
       fn display(&self) -> Cow<&str>;
    struct MyItem {
       .... many interesting fields.
    impl SkimItem for MyItem {...}
    let (tx, rx) = channel::<MyItem>();
    // Launch a thread, send MyItems as they are generated into `tx`.
    let mut skim = Skim::with_sender(rx, SkimOptionsBuilder::new().multi_select(true).ignore_case(true));
    let result = skim.query_user();  // Consumes `sk`.
    /// Join the producing thread. `result` is now either an Err or a Vec<SkimItem>, so I know what was selected.

    I looked through the code and it seemed like this was not very easy to do right now, since the clap::ArgMatches is passed around instead of option structs and the UI and filtering code is a bit interleaved. Do you think this is a feasible feature request?

    opened by SirVer 12
  • Ignoring files

    Ignoring files

    It would be nice to be able to ignore certain files (e.g., dotfiles). It would be really nice if fzf-rs could read .gitignore etc (like ack, ag, and friends).

    opened by Stebalien 9
  • [ Fedora ] Error: Unable to find a match: skim

    [ Fedora ] Error: Unable to find a match: skim

    ❯ sudo dnf install skim
    Last metadata expiration check: 4:59:00 ago on Wed 24 Jun 2020 17:13:12 +0530.
    No match for argument: skim
    Error: Unable to find a match: skim
    opened by kaushalyap 8
  • Exact matching mode without case sensitivity?

    Exact matching mode without case sensitivity?

    It seems the fuzzy matching mode is always case insensitive, and the exact matching mode is always case sensitive.

    However, is there any way to get the contiguous matching mode that exact provides (i.e. match whole words) while not having to also match the case? In other words, is it possible to set exact mode to case insensitivity, or smart-case?

    For explanation: My use case is full-text searching my notes (with rg) and then searching over the output with skim. Matching the output with the fuzzy matcher mostly shows completely unrelated notes, and exact matching shows the perfect answers except for always having to search for the right case.

    opened by marty-oehme 8
  • [Library] nth option is broken?

    [Library] nth option is broken?


    extern crate skim;
    use skim::prelude::*;
    use std::io::Cursor;
    pub fn main() {
        let input = "foo 123";
        let options = SkimOptionsBuilder::default()
        let item_reader = SkimItemReader::default();
        let items = item_reader.of_bufread(Cursor::new(input));
        let selected_items = Skim::run_with(&options, Some(items))
            .map(|out| out.selected_items)
            .unwrap_or_else(|| Vec::new());
        for item in selected_items.iter() {
            print!("{}{}", item.output(), "\n");
    cargo run --example nth

    It generates a match foo 123 although it shouldn't match anything (2nd field consists of numbers, we match on a letter). Am I doing anything wrong?

    skim in terminal works as expected

    > echo "foo 123" | sk --nth=1 --filter f
    foo 123
    > echo "foo 123" | sk --nth=2 --filter f
    # no output, exit code 1
    opened by murlakatamenka 7
  • skim panic - attempt to unwrap None value

    skim panic - attempt to unwrap None value

    Using a function like this and selecting some process (just tried random until it broke) as skims' selection produces a panic:

    $ fkill
    thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', 27
    function fkill -d 'fuzzy kill processes'
      ps -ef | sed 1d | sk --multi
    opened by bryaan 7
  • Integration with `ripgrep` not working

    Integration with `ripgrep` not working

    I have sk 0.9.1 and rg 12.1.1 installed, both via cargo-install. Both sk and rg are in my path, and they work well independently. But I cannot integrate them with sk --ansi -i -c 'rg --color=always --line-number "{}"'. Nevertheless, sk is working with grep. ( I don't have ag or ack so I cannot test these ).

    opened by lebensterben 6
  • Skim not working properly with ansi input

    Skim not working properly with ansi input


    fd --color always | sk --ansi

    Displays most colors, but not for current-directory files seemingly, whereas

    fd --color always | fzf --ansi

    Does what I expect. I'm not sure exactly where this issue is, but I'm noticing it now using this as my default file finder over fzf.

    opened by johnpyp 6
  • Support tiebreak based on length of original text.

    Support tiebreak based on length of original text.

    Hi @lotabout thanks for skim I'm enjoying it.

    One thing I have wished for is tiebreaking based on the length of the original text, in my case shorter strings are preferred. This lets me select longer strings by continuing to type in Vim rather than use the arrow keys I have included an example below of the default behaviour of skim. In this example lets say we are selecting the build_script_mod directory by typing mod.

    Before, just sk: Screen Shot 2019-09-23 at 10 57 20 pm

    After, running sk --tiebreak=score,length: Screen Shot 2019-09-23 at 11 09 17 pm

    This may accidentally help with https://github.com/lotabout/skim/issues/213.

    opened by aldhsu 6
  • No more executables in releases?

    No more executables in releases?

    In the README, it says in installation section that one can head to releases to get ready executables. I went there and there's only zipped source code.

    I scrolled down and it seems that back in the day, exe used to come with releases but not any more, can we have them again please?

    opened by thisismygitrepo 0
  • Implement fzf's --info option

    Implement fzf's --info option

    I use sk as a drop-in replacement for fzf. It worked fine until recently I use zoxide's interactive mode 'zi'. It relies on 'fzf --info' option to work. It would be great to have both rust applications works together.

    opened by zou000 0
  • Performance improvements: `sk` now comparable to `fzf`

    Performance improvements: `sk` now comparable to `fzf`

    Benchmark 1: sk --query=hopscotchbubble -e --exit-0 < ../countwords/kjvbible_x10.txt
      Time (mean ± σ):     115.2 ms ±   0.6 ms    [User: 247.2 ms, System: 36.0 ms]
      Range (min … max):   113.6 ms … 116.4 ms    25 runs
    Benchmark 2: fzf --query=hopscotchbubble -e --exit-0 < ../countwords/kjvbible_x10.txt
      Time (mean ± σ):     119.9 ms ±   6.9 ms    [User: 230.1 ms, System: 63.0 ms]
      Range (min … max):    93.6 ms … 124.4 ms    25 runs
      'sk --query=hopscotchbubble -e --exit-0 < ../countwords/kjvbible_x10.txt' ran
        1.04 ± 0.06 times faster than 'fzf --query=hopscotchbubble -e --exit-0 < ../countwords/kjvbible_x10.txt'

    I've made some improvements to sk so its baseline performance (--exact matching) is now comparable to/better than fzf on the M1 for larger inputs (and less than 5% on Intel). sk still lags way behind in fuzzy searches (10%-20% more wall clock time and 2x CPU time).

    We also now use less memory than fzf.

    I was also able to effectively end the sometimes laggy inputs on searches on over ~200,000 items by moving the matcher to its own thread pool. This is most important to me as now sk is a usable as a daily driver.

    And I'm pretty certain I haven't made any breaking changes to the public API.

    If anyone is interested, I will merge these changes back into skim. But as it is -- it seems interest is waning -- I may consider a hard fork under a new name.

    Crazy respect for how fast fzf is. My guess is two things make fzf still very fast at fuzzy matching on large inputs: 1) as much as it pains me to say, golang is better at/easier for at this type of concurrency than rayon/crossbeam, and 2) the fzf fuzzy algo is amazing (but also may be "cheating"/bailing out somehow on large inputs?).

    My question is -- does anyone want to continue, or should I try a fork?

    opened by kimono-koans 0
  • support : in filenames

    support : in filenames


    Is there any chance to support : in filenames? Having a bunch of files like form:blabla.ini . Field matching chokes on that, because of the : in the filename. execute-silent also chokes (for me) with

    --bind 'ctrl-l:execute-silent(/opt/sublime_text/sublime_text {..2}),f3:execute-silent(/opt/sublime_text/sublime_text {..2}),f4:execute(komodo {1}#{2})'

    Do I miss something?

    opened by agroszer 0
Jinzhou Zhang
A Program is a Process, Not a Thing
Jinzhou Zhang
minimalistic command launcher in rust

rrun Note: Apart from the occasional fix, this project is not actively developed anymore. rrun works fine and should run/compile for the time being on

null 105 Nov 18, 2022
Yet another fancy watcher. (Rust)

funzzy Yet another fancy watcher. (Inspired by antr / entr) Configure execution of different commands using semantic yaml. # .watch.yaml # list here a

Cristian Oliveira 188 Dec 12, 2022
A modern replacement for ps written in Rust

procs procs is a replacement for ps written in Rust. Documentation quick links Features Platform Installation Usage Configuration Features Output by t

null 3.6k Jan 5, 2023
A more intuitive version of du in rust

Dust du + rust = dust. Like du but more intuitive. Why Because I want an easy way to see where my disk is being used. Demo Install Cargo cargo install

andy.boot 5.5k Jan 8, 2023
Blazing 💥 fast terminal-ui for git written in rust 🦀

Blazing fast terminal client for git written in Rust Features Fast and intuitive keyboard only control Context based help (no need to memorize tons of

Stephan Dilly 11.8k Jan 5, 2023
A simple and fast download accelerator, written in Rust

zou A simple and fast download accelerator, written in Rust Zou is a Snatch fork by @k0pernicus. Snatch is a fast and interruptable download accelerat

Antonin Carette 173 Dec 4, 2022
A bash-like Unix shell written in Rust

Cicada Unix Shell Cicada is a simple Unix shell written in Rust. Documents Install cicada Environment Variables Cicada Builtins Completion RC File His

Hugo Wang 921 Dec 28, 2022
Performs distributed command execution, written in Rust w/ Tokio

Concurr: Distributed and Concurrent Command Execution, in Rust This project is dual licensed under MIT and Apache 2.0. Originally inspired by the GNU

Michael Murphy 93 Dec 18, 2022
A library to listen to global hotkeys in Rust

Rust Hotkey A library to listen to global hotkeys in Rust How to use See the examples folder for how to use this library. OS Support This lib aims to

James Birtles 44 Dec 12, 2022
🔮 Futuristic take on hexdump, made in Rust.

hex (hx) Futuristic take on hexdump. hx accepts a file path as input and outputs a hexadecimal colorized view to stdout. $ hx tests/files/alphanumeric

Julian Sitkevich 387 Dec 27, 2022
Cross-platform Rust rewrite of the GNU coreutils

uutils coreutils uutils is an attempt at writing universal (as in cross-platform) CLI utilities in Rust. This repository is intended to aggregate GNU

null 13k Dec 30, 2022
A TUI system monitor written in Rust

NO LONGER MAINTAINED. For a similar program, check out https://github.com/ClementTsang/bottom. ytop Another TUI based system monitor, this time in Rus

Caleb Bassi 2.1k Jan 3, 2023
A rust layered configuration loader with zero-boilerplate configuration management.

salak A layered configuration loader with zero-boilerplate configuration management. About Features Placeholder Key Convension Cargo Features Default

Daniel YU 28 Sep 20, 2022
Untrusted IPC with maximum performance and minimum latency. On Rust, on Linux.

Untrusted IPC with maximum performance and minimum latency. On Rust, on Linux. When is this Rust crate useful? Performance or latency is crucial, and

null 72 Jan 3, 2023
An over-engineered rewrite of pipes.sh in Rust

pipes-rs An over-engineered rewrite of pipes.sh in Rust Installlation macOS Install using Homebrew or download manually from releases. $ brew install

Lucas 301 Dec 30, 2022
Trup-rewrite in rust! Finally!

Trup, but Rust! A Discord bot for the Unixporn community Now written in a good language! Dependencies Rust nightly sqlx-cli (if you need to change the

r/unixporn 106 Dec 22, 2022
A collection of small Rust programs for doing weird things

This is a repo of small programs, proof of concepts, or templates written in Rust that relate in some way to hacking and/or CTF. I think Rust is real

d3npa 22 Nov 9, 2022
🍂 A Rust-based simulated DOM (browser-independent replacement for web_sys)

DOM in Rust without a browser Hello and welcome. This library provides server-side or browserless simulation of a DOM. Example Usage use std::sync::Ar

Philip Peterson 45 Dec 13, 2022
Rust implementation to simply convert between coordinate systems

GeoMorph Simple conversion between different coordinate systems without external wrappers

Victor Lopes 10 Nov 8, 2022