Using FZF to Improve Productivity
By Akash Khan
Consider the following problem.
Given an array of strings find all strings that match a given pattern. For example consider the input array to be ['apple', 'ball', 'cat', 'doll']
and the pattern to be al
.
Looking at the array we can see that ball
is the only string that matches the pattern exactly and it's not very difficult to implement an algorithm to detect this. However a much more interesting version of the problem is when we remove the restriction that the characters in the pattern should appear together. In the above example it would mean that our algorithm would need to output ['apple' , 'ball']
.
This is known as fuzzy matching. It is part of a larger group of problems known as Approximate String Matching
FZF is a commandline fuzzy finder which solves the above problem . It reads the list from STDIN
and writes the result to STDOUT
. What makes it special though is that it is extremely fast and extensible. Combined with Unix pipelines we can create some very useful functions.
Setting up and basic usage
Check out the github page for installation instructions. It’s available for every major platform and can be setup very easily. Once done you should have the fzf
executable added.
Using fzf
is very easy. All the commands I will be talking about follow the same structure .
- First you have the input command which generates the input list .
- Second you pass this list to the
fzf
executable using|
operator andfzf
will generate the UI for pattern matching. - Once the user inputs the pattern
fzf
will pass the results toSTDOUT
and we can use the|
operator again to use these results.
In general
command 1 {options} | command 2 ... | fzf {options} | command n | command n+1 ...
For example
Advanced Usage
Since fzf is built to be extensible, it works very well with other unix commands and by composing different commands we can create powerful interfaces. I have listed a few which I’ve been using below:
- Navigating directories ( Replacing
cd
)
sh function fd() {
local dir dir=$(find ${1:-.} -path ‘*/\.*’ -prune \ -o -type d -print 2> /dev/null | fzf +m) && cd “$dir”
}
function fdr() {
local declare dirs=()
## Recursively fetch parent directories
get_parent_dirs() {
if [[ -d "${1}" ]]; then dirs+=("$1"); else return; fi
if [[ "${1}" == '/' ]]; then
for _dir in "${dirs[@]}"; do echo $_dir; done
else
get_parent_dirs $(dirname "$1")
fi
}
local DIR=$(get_parent_dirs $(realpath "$PWD") | fzf +m)
cd "$DIR"
}
- Killing processes
fkill() {
local pid
pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}') if [ "x$pid" != "x" ]
then
echo $pid | xargs kill -9
fi
}
- Searching git commits
function gitlog() {
## git log -> fzf -> extract commit SHA -> git show
git log --graph --color=always \
--format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
fzf --ansi --no-sort --reverse --tiebreak=index --toggle-sort=\` \
--bind "ctrl-m:execute:
echo '{}' | grep -o '[a-f0-9]\{7\}' | head -1 |
xargs -I % sh -c 'git show --color=always % | less -R'"
}
If you are a Vim/Neovim user like me, you would be glad to know that FZF also comes with a built-in integration for Vim which can be used to navigate through your code base!
Conclusion
The above scripts are just a small subset of the things we can do with fzf
. By composing different commands, we can create very powerful interfaces which can help save time. The FZF Wiki page contains some very good documentation and examples for getting started.