In a previous blog post I’ve written, I had mentioned fzf
as a utility that is very useful to have in your toolbox. I’ll be providing some tips and code snippets that can be used to help improve your workflow in the terminal and also how to create your own fzf
-based workflow. You can find a collection of some of these in my shared dotfiles repository or in the fzf
examples wiki page.
git checkout
There are many reasons for us to switch between branches. We might want to pull down a co-worker’s branch locally for testing/review purposes. Or we may need to address feedback from peers to improve workflow. Because of this, I often see people spending time looking for the branch name to pull down, trying to remember what it was named, or using autocomplete to tab through all of the branches locally. The below screencast demonstrates using fzf
with the code snippet provided on how much easier it can be to switch branches and have context as well.
gcb() {
result=$(git branch -a --color=always | grep -v '/HEAD\s' | sort |
fzf --height 50% --border --ansi --tac --preview-window right:70% \
--preview 'git log --oneline --graph --date=short --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// <<< {} | cut -d" " -f1) | head -'$LINES |
sed 's/^..//' | cut -d' ' -f1)
if [[ $result != "" ]]; then
if [[ $result == remotes/* ]]; then
git checkout --track $(echo $result | sed 's#remotes/##')
else
git checkout "$result"
fi
fi
}
In the above code block, notice that we can do a fuzzy search against the branch name without having to ensure that we spelled the branches correctly. And, we don’t have to constantly do tab completion to narrow down the results. Another benefit: we can also view the git history of the branch on the side. to determine if we are looking for the correct branch.
git status
Sometimes, we want to look at the current state of the branch we are working on and perform certain actions the files modified. The following snippet allows us to take a peek at the diff of each file returned by git status
and allow us to perform various actions upon them. The --multi
flag allows us to also decide which files we want to perform actions on.
gf() {
git -c color.status=always status --short |
fzf --height 50% --border --ansi -multi --ansi --nth 2..,.. \
--preview '(git diff --color=always -- {-1} | sed 1,4d; cat {-1}) | head -500' |
cut -c4- | sed 's/.* -> //'
}
In the above, we still have the option to perform fuzzy search against the files listed. However, we also have a diff displayed on the right side associated with the selected file. With the --multi
flag, we are able to perform actions on any file selected and perform a bulk action on it. Some of the various actions would be performing a git add
on the selected files to changes for those files, editing them, or something else. Also, it is worth mentioning that the --preview
panel on the right is scrollable with the mouse scroll wheel or with bindable actions, which is shown briefly in the screen capture above.
ps kill
There are times when I need to kill certain running processes due to various reasons. It isn’t ideal, but it does happen every now and then. Make it easier to kill multiple process by using fuzzy matching, and selecting the ones that we wish to kill. Use the following snippet:
nuke() {
local pid
pid=$(ps -ef | grep -v ^root | sed 1d | fzf -m | awk '{print $2}')
if [ "x$pid" != "x" ]
then
echo $pid | xargs kill -${1:-9}
fi
}
Writing Your Own…
The simplest way to explain how fzf
works is that it performs the following:
- takes input piped into
fzf
. - allows you to perform fuzzy searching and returns any “selected” entry.
- the selected entries are returned as the output of
fzf
with each selection on its own line.
So essentially, what we want to have as the output should be what we pipe into fzf
. It is important that it contains all the data you might possibly need to execute against. I explain this a bit more with code comments in the example below. We will take the following example and break it down:
sys-start() {
sudo systemctl list-unit-files | grep disabled | awk '{print $1}' | fzf -m |
while read unit; do
sudo systemctl start $unit &&
journalctl -u $unit --since "10 sec ago" --no-pager
done
}
With our basic understanding that fzf
accepts stdin
input, let’s break up what is going on above….
sudo systemctl list-unit-files | grep disabled | awk '{print $1}'
It will take the initial result of:
nginx.service disabled
postgres.service disabled
sshd.service disabled
to
nginx.service
postgres.service
sshd.service
With the above, fzf
will do fuzzy matching on each line to determine which you want to perform an action upon. The string
that is displayed on the selected/highlighted line will be what is returned from fzf
upon selection. So, if we had selected abc.service
, then we would get abc.service
as the output. Upon getting that output, we’d perform an action on it to increase our workflow strength.
We're building an AI-powered Product Operations Cloud, leveraging AI in almost every aspect of the software delivery lifecycle. Want to test drive it with us? Join the ProdOps party at ProdOps.ai.