Tools: Fuzzy Find Everything

Tools: Fuzzy Find Everything

What is fuzzy search

How to get it

Standard functions

Power User: keybinds and shell integration

File previews

Find and execute scripts

Find and edit files

Adding keybinds Imagine searching, editing, and executing files in your terminal so quickly that the friction disappears. You type a loose, typo-filled query like "contrler bugfix" — and instantly see "controller_fix_2025.py" rise to the top of the list, even in a monorepo with thousands of files. No exact spelling required. No tab-mashing through long paths. Just start typing what you half-remember, watch the results narrow in real time, and hit Enter. fzf turns any list piped into it (files, command history, git branches, running processes, you name it) into a fast, interactive fuzzy finder. It handles massive directories in milliseconds and makes navigation feel almost thought-to-action. Traditional tools demand precision: fzf forgives all of that. Type loosely, in any order, with gaps or abbreviations — it ranks intelligently and shows context so you can decide confidently. Fuzzy search finds approximate matches instead of demanding exact strings. Type "contrler bugfix" and it finds "controller_fix_2025.py" even with typos, skipped letters, out-of-order words, or abbreviations.

Traditional tools like ls | grep, tab-completion, or find require precise spelling and full paths – frustrating when you half-remember a filename or are working in massive projects.fzf is the interactive command-line fuzzy finder that turns any list (files, history, git branches, processes…) into a fast, beautiful, searchable interface. Pipe anything into it via stdin, start typing loosely, and hit Enter – it handles huge directories in milliseconds.Bonus: It's not just files. fzf supports Ctrl+R history search, directory jumping, killing processes, and more. Once set up, it becomes muscle memory.In this guide, we'll install fzf, enable its standard functions, then go deep into previews, custom scripts, and keybinds that make your terminal feel FAST. Installing fzf with your package manager is the most straight forward method. There are also package managers for Windows.Chocolatey, if you have admin access: or Scoop if you don't: And macOS can install with Homebrew The most up to date releases for all operating systems are also available here The newer versions of fzf come packaged with standard keybinds and search scripts.You can enable them on linux with: Reload your shell (source ~/.bashrc) or restart terminal.This unlocks: Ctrl+T → fuzzy file search in current dir (inserts path)Ctrl+R → fuzzy command history searchAlt+C → fuzzy cd to subdirectory Stop here and your searching is already superior than stock. Keep reading for previews, custom scripts, and global keybinds. If you're not afraid to get your hands a bit dirty, fzf can become very powerful.This section is about minimizing the time it takes to get where the work is. Using the --preview flag, you can see what you're about to open, edit, or execute. Here's a simple example using the preview flag to try out: It will try to show you what's in the file while you're searching.This is pretty good, but we can do better: This one shows the same text, but with syntax highlighting and line numbers.It uses bat or batcat to do the highlighting and numbers.To install bat: This is the exact command that's executing in the above screenshot: That's one long command!This is what's going on:First it calls the find command, fd, to search for files.The second line takes the results and gives it to fzf.And we've already seen that the preview flag and bat give highlighted outputs. I'll be relying on this command for the rest of the article.Getting this to work as pictured above, you'll need to install a few more programs: fd or fd-find is a fast search program.And rg or ripgrep is a fast regular expression search tool. We're going to use the above command and execute scripts with a function in .bashrc. Now, typing fzf_execute in your terminal will start the search.The if statement writes the path of the selected file to a temporary location and calls a script. "fzf_script.sh" will take the file path an determine what filetype it is and open it.So if it's a media file, it'll open it with vlc; text file, vim; and so on.For python and bash scripts (or any other you choose), it will try execute them. Now, I can type "fzf_script" in my terminal, search for a docx file, and launch libreoffice to open it.Changing fzf_script.sh will be necessary to use your preferred programs like openoffice or kate. If you want to edit files in your favorite text editor, add another .bashrc function. Notice our find command is almost the same as before, just excluding less files.We also call a different script in our if statement. It will try to open the selected file in a text editor only. fzf is most useful when the functions and scripts above are triggered from simple key binds. In your .bashrc add these at the bottom: "bind -r" will remove all functionality currently associated with that key combination."bind -x" will then add the functionality you want. The first line removes all functionality associated with ctrl-x.The second line associates the ctrl-x key combination with the bash function fzf_script. Now, while at a terminal, pressing ctrl-x will start your search to open or execute any file.

Pressing ctrl-f will now start your search to edit any file. From here, you should be able to add your own customized keybinds and scripts. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# Debian and Ubuntu -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install fzf # Fedora -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install fzf # Arch -weight: 600;">sudo -weight: 500;">pacman -S fzf # Gentoo emerge --ask app-shells/fzf # OpenSUSE -weight: 600;">sudo zypper i fzf # Debian and Ubuntu -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install fzf # Fedora -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install fzf # Arch -weight: 600;">sudo -weight: 500;">pacman -S fzf # Gentoo emerge --ask app-shells/fzf # OpenSUSE -weight: 600;">sudo zypper i fzf # Debian and Ubuntu -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install fzf # Fedora -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install fzf # Arch -weight: 600;">sudo -weight: 500;">pacman -S fzf # Gentoo emerge --ask app-shells/fzf # OpenSUSE -weight: 600;">sudo zypper i fzf choco -weight: 500;">install fzf -y choco -weight: 500;">install fzf -y choco -weight: 500;">install fzf -y scoop -weight: 500;">install fzf scoop -weight: 500;">install fzf scoop -weight: 500;">install fzf -weight: 500;">brew -weight: 500;">install fzf -weight: 500;">brew -weight: 500;">install fzf -weight: 500;">brew -weight: 500;">install fzf echo 'eval "$(fzf --bash)"' >> ~/.bashrc echo 'eval "$(fzf --bash)"' >> ~/.bashrc echo 'eval "$(fzf --bash)"' >> ~/.bashrc fzf --preview 'cat {}' fzf --preview 'cat {}' fzf --preview 'cat {}' fzf --preview 'bat --color=always --style=numbers {}' fzf --preview 'bat --color=always --style=numbers {}' fzf --preview 'bat --color=always --style=numbers {}' -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install bat -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install bat -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install bat fd --type f --hidden --exclude .-weight: 500;">git . / 2>/dev/null | \ fzf --height 75% --layout=reverse --border --preview \ 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi' fd --type f --hidden --exclude .-weight: 500;">git . / 2>/dev/null | \ fzf --height 75% --layout=reverse --border --preview \ 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi' fd --type f --hidden --exclude .-weight: 500;">git . / 2>/dev/null | \ fzf --height 75% --layout=reverse --border --preview \ 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi' -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install bat fd-find ripgrep -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install bat fd-find ripgrep -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install bat fd-find ripgrep fzf_execute() { script=$(fd --type f --hidden --exclude .-weight: 500;">git --exclude /proc --exclude / sys . / 2>/tmp/fzf_fd_errors.log | fzf --height 75% --layout=reverse --border --preview 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi') if [[ -n "$script" ]]; then echo "$script" > /tmp/fzf_selected_script $HOME/Configs/fzf/fzf_execute.sh fi } fzf_execute() { script=$(fd --type f --hidden --exclude .-weight: 500;">git --exclude /proc --exclude / sys . / 2>/tmp/fzf_fd_errors.log | fzf --height 75% --layout=reverse --border --preview 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi') if [[ -n "$script" ]]; then echo "$script" > /tmp/fzf_selected_script $HOME/Configs/fzf/fzf_execute.sh fi } fzf_execute() { script=$(fd --type f --hidden --exclude .-weight: 500;">git --exclude /proc --exclude / sys . / 2>/tmp/fzf_fd_errors.log | fzf --height 75% --layout=reverse --border --preview 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi') if [[ -n "$script" ]]; then echo "$script" > /tmp/fzf_selected_script $HOME/Configs/fzf/fzf_execute.sh fi } #!/bin/bash ######################### # Reads a path to file # # from tmp and tries to # # execute it; .sh .py # # # # tmp is fed by fzf f() # # out in .bashrc # ######################### if [[ ! -f /tmp/fzf_selected_script ]]; then exit 1 fi selected_script=$(cat /tmp/fzf_selected_script) # Convert relative path to absolute path selected_script=$(realpath "$selected_script" 2>/dev/null) if [[ ! -f "$selected_script" ]]; then exit 1 fi rm -f /tmp/fzf_selected_script # Get the file extension (lowercase) extension=$(echo "${selected_script##*.}" | tr '[:upper:]' '[:lower:]') # Function to open/execute the file based on its extension open_file() { case "$extension" in sh) # Execute bash scripts if [[ -x "$selected_script" ]]; then "$selected_script" else bash "$selected_script" fi ;; py) # Run Python scripts python3 "$selected_script" ;; xlsx|ods|csv) # Open spreadsheets with LibreOffice Calc libreoffice --calc "$selected_script" & ;; docx|odt) # Open documents with LibreOffice Writer libreoffice --writer "$selected_script" & ;; pdf) # Open PDFs with default PDF viewer xdg-open "$selected_script" & ;; jpg|jpeg|png|gif) # Open images with default image viewer gimp "$selected_script" & ;; mp4|mov|avi|mkv|mp3|flac) # Open videos with default video player vlc "$selected_script" & ;; txt|md|log) # Open text files with default text editor vim "$selected_script" & ;; *) # Fallback: Try to open with default application if command -v xdg-open >/dev/null 2>&1; then xdg-open "$selected_script" & else echo "Error: No handler found for file extension '$extension'" exit 1 fi ;; esac } # Check if the file is executable and run directly if it is if [[ -x "$selected_script" && "$extension" != "sh" ]]; then "$selected_script" else open_file fi #!/bin/bash ######################### # Reads a path to file # # from tmp and tries to # # execute it; .sh .py # # # # tmp is fed by fzf f() # # out in .bashrc # ######################### if [[ ! -f /tmp/fzf_selected_script ]]; then exit 1 fi selected_script=$(cat /tmp/fzf_selected_script) # Convert relative path to absolute path selected_script=$(realpath "$selected_script" 2>/dev/null) if [[ ! -f "$selected_script" ]]; then exit 1 fi rm -f /tmp/fzf_selected_script # Get the file extension (lowercase) extension=$(echo "${selected_script##*.}" | tr '[:upper:]' '[:lower:]') # Function to open/execute the file based on its extension open_file() { case "$extension" in sh) # Execute bash scripts if [[ -x "$selected_script" ]]; then "$selected_script" else bash "$selected_script" fi ;; py) # Run Python scripts python3 "$selected_script" ;; xlsx|ods|csv) # Open spreadsheets with LibreOffice Calc libreoffice --calc "$selected_script" & ;; docx|odt) # Open documents with LibreOffice Writer libreoffice --writer "$selected_script" & ;; pdf) # Open PDFs with default PDF viewer xdg-open "$selected_script" & ;; jpg|jpeg|png|gif) # Open images with default image viewer gimp "$selected_script" & ;; mp4|mov|avi|mkv|mp3|flac) # Open videos with default video player vlc "$selected_script" & ;; txt|md|log) # Open text files with default text editor vim "$selected_script" & ;; *) # Fallback: Try to open with default application if command -v xdg-open >/dev/null 2>&1; then xdg-open "$selected_script" & else echo "Error: No handler found for file extension '$extension'" exit 1 fi ;; esac } # Check if the file is executable and run directly if it is if [[ -x "$selected_script" && "$extension" != "sh" ]]; then "$selected_script" else open_file fi #!/bin/bash ######################### # Reads a path to file # # from tmp and tries to # # execute it; .sh .py # # # # tmp is fed by fzf f() # # out in .bashrc # ######################### if [[ ! -f /tmp/fzf_selected_script ]]; then exit 1 fi selected_script=$(cat /tmp/fzf_selected_script) # Convert relative path to absolute path selected_script=$(realpath "$selected_script" 2>/dev/null) if [[ ! -f "$selected_script" ]]; then exit 1 fi rm -f /tmp/fzf_selected_script # Get the file extension (lowercase) extension=$(echo "${selected_script##*.}" | tr '[:upper:]' '[:lower:]') # Function to open/execute the file based on its extension open_file() { case "$extension" in sh) # Execute bash scripts if [[ -x "$selected_script" ]]; then "$selected_script" else bash "$selected_script" fi ;; py) # Run Python scripts python3 "$selected_script" ;; xlsx|ods|csv) # Open spreadsheets with LibreOffice Calc libreoffice --calc "$selected_script" & ;; docx|odt) # Open documents with LibreOffice Writer libreoffice --writer "$selected_script" & ;; pdf) # Open PDFs with default PDF viewer xdg-open "$selected_script" & ;; jpg|jpeg|png|gif) # Open images with default image viewer gimp "$selected_script" & ;; mp4|mov|avi|mkv|mp3|flac) # Open videos with default video player vlc "$selected_script" & ;; txt|md|log) # Open text files with default text editor vim "$selected_script" & ;; *) # Fallback: Try to open with default application if command -v xdg-open >/dev/null 2>&1; then xdg-open "$selected_script" & else echo "Error: No handler found for file extension '$extension'" exit 1 fi ;; esac } # Check if the file is executable and run directly if it is if [[ -x "$selected_script" && "$extension" != "sh" ]]; then "$selected_script" else open_file fi fzf_edit() { file=$(fd --type f --hidden --exclude .-weight: 500;">git . / 2>/dev/null | fzf --height 75% --layout=reverse --border --preview 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi') if [[ -n "$file" ]]; then echo "$file" > /tmp/fzf_selected_file ~/Configs/fzf/fzf_vim.sh fi } fzf_edit() { file=$(fd --type f --hidden --exclude .-weight: 500;">git . / 2>/dev/null | fzf --height 75% --layout=reverse --border --preview 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi') if [[ -n "$file" ]]; then echo "$file" > /tmp/fzf_selected_file ~/Configs/fzf/fzf_vim.sh fi } fzf_edit() { file=$(fd --type f --hidden --exclude .-weight: 500;">git . / 2>/dev/null | fzf --height 75% --layout=reverse --border --preview 'if file --mime-type {} | rg -q text; then bat --color=always --style=numbers {}; else cat {}; fi') if [[ -n "$file" ]]; then echo "$file" > /tmp/fzf_selected_file ~/Configs/fzf/fzf_vim.sh fi } #!/bin/bash ######################### # Reads file path from # # tmp and tries to open # # file in vim # # # # tmp fed by fzf f() in # # .bashrc # ######################### if [[ ! -f /tmp/fzf_selected_file ]]; then echo "No file selected by fzf" exit 1 fi selected_file=$(cat /tmp/fzf_selected_file) vim -u ~/Configs/.vimrc "${selected_file}" rm -f /tmp/fzf_selected_file #!/bin/bash ######################### # Reads file path from # # tmp and tries to open # # file in vim # # # # tmp fed by fzf f() in # # .bashrc # ######################### if [[ ! -f /tmp/fzf_selected_file ]]; then echo "No file selected by fzf" exit 1 fi selected_file=$(cat /tmp/fzf_selected_file) vim -u ~/Configs/.vimrc "${selected_file}" rm -f /tmp/fzf_selected_file #!/bin/bash ######################### # Reads file path from # # tmp and tries to open # # file in vim # # # # tmp fed by fzf f() in # # .bashrc # ######################### if [[ ! -f /tmp/fzf_selected_file ]]; then echo "No file selected by fzf" exit 1 fi selected_file=$(cat /tmp/fzf_selected_file) vim -u ~/Configs/.vimrc "${selected_file}" rm -f /tmp/fzf_selected_file bind -r '\C-x' bind -x '"\C-x": fzf_execute' bind -r '\C-f' # Remove current binding bind -x '"\C-f": fzf_edit' # -weight: 500;">start file search function bind -r '\C-x' bind -x '"\C-x": fzf_execute' bind -r '\C-f' # Remove current binding bind -x '"\C-f": fzf_edit' # -weight: 500;">start file search function bind -r '\C-x' bind -x '"\C-x": fzf_execute' bind -r '\C-f' # Remove current binding bind -x '"\C-f": fzf_edit' # -weight: 500;">start file search function - ls | grep needs correct spelling and order - Tab completion struggles with typos or forgotten segments - find requires full paths or complex flags