So you've installed `fzf` – now what?
andrew-quinn.me
andrew-quinn.me
I use atuin[0] instead of fzf as I find the experience a bit nicer and it has history backups built in (disclaimer, I am a maintainer)
Some of our users still prefer fzf because they are used to how it fuzzy finds, but we're running an experiment with skim[1] which allows us to embed the fuzzy engine without much overhead - hopefully giving them back that fzf-like experience
[0]: https://github.com/ellie/atuin [1]: https://github.com/lotabout/skim
For me another issue was that I needed to do more keystrokes for the same behaviour (search a previously ran command and execute it).
Fuzzy shell history search is just one of those mind-blowing things. I love my history, it's such a trove and I can trust it to work as some kind of external memory (it's enough to vaguely know the kubectl command, or that I want to "du -h | sort" to see what is using disk space, etc).
> backup and sync encrypted shell history
But it's up to you. You can disable any sync features by installing with `--no-default-features --features client` set. Your application won't have any networking features built into the binary then
It was quite annoying so we're sorry for that
* We use heuristic based searches whereas mcfly trains a neural network * We offer a sync functionality to share history on multiple machines
mcfly is a great project, although they are looking for new maintainers apparently!
Went back to FZF (in Zsh).
I have a seven year zsh history. That may be a contributing factor for the issues ?
We introduced this as we found a lot of people reporting bugs that had already been fixed + they just needed to update, or users on the sync server that were >1yr behind on updates (making improvements really difficult to introduce).
But if you can't get past the double-enter, then I understand.
I think there was some work to fix that, I'll check up on it. Thanks for the reminder
Zsh snippet below in case it's helpful to anybody. With this in your .zshrc ctrl-r will search your shell history with fzf+atuin and ctrl-e will bring up atuin's own fuzzy finder in case you still want it.
It only searches the last 5000 entries of your atuin history for speed, but you can tweak ATUIN_LIMIT to your desired value if that's not optimal.
atuin-setup() {
if ! which atuin &> /dev/null; then return 1; fi
bindkey '^E' _atuin_search_widget
export ATUIN_NOBIND="true"
eval "$(atuin init "$CUR_SHELL")"
fzf-atuin-history-widget() {
local selected num
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2>/dev/null
# local atuin_opts="--cmd-only --limit ${ATUIN_LIMIT:-5000}"
local atuin_opts="--cmd-only"
local fzf_opts=(
--height=${FZF_TMUX_HEIGHT:-80%}
--tac
"-n2..,.."
--tiebreak=index
"--query=${LBUFFER}"
"+m"
"--bind=ctrl-d:reload(atuin search $atuin_opts -c $PWD),ctrl-r:reload(atuin search $atuin_opts)"
)
selected=$(
eval "atuin search ${atuin_opts}" |
fzf "${fzf_opts[@]}"
)
local ret=$?
if [ -n "$selected" ]; then
# the += lets it insert at current pos instead of replacing
LBUFFER+="${selected}"
fi
zle reset-prompt
return $ret
}
zle -N fzf-atuin-history-widget
bindkey '^R' fzf-atuin-history-widget
}
atuin-setupI think what would be more useful is to record `env` or some similar context along with the time and command. That would probably get weird pretty fast, though. Maybe just a thing that could insert some useful bookmarking/state into the history record on-demand? `history-set-checkpoint` or something would save your pwd and local vars or something.
With the current directory you should be able to get the absolute path from your relative paths
Or maybe I'm just lazy.
Or at least I hope it is. :)
but that isn't sexy somehow. we'll have to settle on 'shag'.
"Ted, the change I suggest doesn't affect the independence of your sessions as you suggest. Each shell maintains a unique history in memory so modifying the history file has no affect on running terminals. The only time the history file is read is when you start a new terminal. I recommend you try my suggestion. Really, all I am doing is eliminating the race condition that causes the bash history file to have inconsistent data.
Thanks for the feedback."
If you do want to load the history persisted from other shells into the current one, all you have to do (if memory serves) is:
$ history -rPROMPT_COMMAND='history -a'
Has always worked for me. Goes in your .bashrc from the FM
PROMPT_COMMAND ¶ If this variable is set, and is an array, the value of each set element is interpreted as a command to execute before printing the primary prompt ($PS1). If this is set but not an array variable, its value is used as a command to execute instead.
To wit:
> It says to put those commands in the .bashrc config:
shopt -s histappend
PROMPT_COMMAND="history -a;$PROMPT_COMMAND"
> The first command changes the history file mode to append and the second configures the history -a command to be run at each shell prompt. The -a option makes history immediately write the current/new lines to the history file.I used to have something like this set up on my Linux laptop - the downside is that seperate shell/terminals/windows/tabs don't keep seperate history - so if you eg start a server in shell one (rails s), start editor in two - then go back to one and ctrl-c out - up arrow will now give you "vim" not "rails s".
The problem compounds if you ping, or curl in another shell etc.
setopt inc_append_historySo, you revisit a window, and you want to start from where you left, but now, you might maybe wade through 100's of commands before you get back to that point in time. There are fixes for this too of course, my point is, that it doesn't come without side effects, and that is maybe why it isn't set as default behaviour. At least in a pre 'fzf/atuin/smenu' world.
I prefer smenu's history search, even if I consider myself a heavy fzf user.
I can't believe people still using a tree view of a deeply nested project and clicking though directories and finally finding a file.
I am not willing to type more than a few characters for any file in the project. Fuzzy finding with quick narrowing FTW.
Learning for me: Brew suggests to install the shortcuts but doesn’t automatically do this. Just activated this and Ctrl-R is a big improvement this way.
export CDPATH="$HOME:$HOME/code/:$(ls -d $HOME/work/*/| tr \\n :)"
so regardless of my cwd I can cd into pretty much any project I'd want to. ~/project% find -type d | wc -l
13946
I access files in a dozen or so folders in this hierarchy regularly, so fuzzy find is an extreme boost for me.I have a branch[0] where I'm trying to do things like reduce the user perceptible lag in search, the initial time of ingest, and add small features I need, etc (all done). I've tried to create PRs where I can, and they go unnoticed and unused.
One other thing I was trying to get a handle on is memory usage. The issue is -- you're implicitly creating objects with static lifetimes everywhere. Now try to refactor that, and there is a trait object held in a struct which depends on another trait object, so good luck figuring out the lifetimes. This is totally fine for a fuzzy finder tool, probably, but less fine when you drop a fuzzy find feature it into an app.
Love to have others interested in skim, and eager to work with anyone with big ideas about how to make it better. I'll have to try out atuin!
What do you mean by maintained? There are commits as recent as yesterday on the project: https://github.com/junegunn/fzf
I liked the way he covered just one function though, which at least might be starters for such operations, and are fully operational, and probably caters for better workflows for most, than without fzf.
Funny thing: I try to stop using Ctrl-R from fzf at the moment, and rather use the one that ships with smenu.
I'm now using atuin for shell history and fzf for fuzzy completion[0], works awesome! As Shell I use zsh with some plugins managed via antigen on my Linux Mint default terminal.
[0]: https://github.com/junegunn/fzf#fuzzy-completion-for-bash-an...
I feel like with a lot of these kinds of tools, if I have to actually actively use them, I forget that they exist ('z'[0] ended up being like this for me) and eventually remove them, but something that directly replaces something I use regularly (but hate the UX of) is perfect for me.
Another bit that helps is that I'm not having to learn/remember something new that I'll only have on my laptop. I'll continue to ctrl+R and get a nicer experience, but if I'm ssh'ed into some random box without fzf, ctrl+R will still work, just with a worse experience.
Also all of the tools discussed in this thread (and more) are listed here: https://github.com/ibraheemdev/modern-unix
It does require installing some version of command-line lua.
I was exactly in the situation the article describes, I installed it, then, didn't find a use case.
But that's because Ubuntu doesn't come with completion enabled by default.
You have to add in your bashrc:
if [ -e /usr/share/doc/fzf/examples/key-bindings.bash ]; then
source /usr/share/doc/fzf/examples/key-bindings.bash
fi
The fzf and ubuntu docs don't mention you have to.Couldn't find easily how to do it with google, but chatgpt saved the day once again.
Then it would just be (in bash anyway):
source /usr/share/fzf/keybindings.bash source /usr/share/doc/fzf/examples/completion.bash
source /usr/share/doc/fzf/examples/key-bindings.bashNot trying to single out fzf here - there are many other tools that do this - but I find this behavior really sad because it makes me very disinclined to trust the tool with anything. A command-line tool should not be trying to auto-update itself or manipulate dotfiles in the user's home directory. It's dangerous and unexpected.
There’s no standardised way to extend interactive shell configuration without just appending to /etc/bashrc.
The other problem with this is that it is hard to disable. What if you want fzf but without the key bindings? FZF could add a mechanism for this, but it doesn’t fully solve the problem, because the FZF script being added to global configuration will always be run before any user config.
In this specific case, it is a limitation of bash (and probably ZSH as well) that there is no simple package-manger compatible extension mechanism for interactive shell plugins which doesn’t sacrifice user control.
if [ -e /usr/share/doc/fzf/examples/completion.bash ]; then
source /usr/share/doc/fzf/examples/completion.bash
fi
/usr/share/doc/fzf/README.Debian explains this, as well as the setup for zsh, fish and vim.[1]: https://github.com/junegunn/fzf#fuzzy-completion-for-bash-an...
git log --oneline | fzf
for example is one of my favorite tricks. Instead of scanning by eye or repeatedly grepping to find something, it's a live fuzzy filter. And depending on how deep you want to go you can then add key bindings to check out the selected commit, or preview the full message, or anything really. git log --oneline | fzf | cowsay
Sky's the limit.Combined with some clever "preview" functions, and a pop-up terminal function (which, that part is admittedly harder than it ought to be) it can be an across the board a better general purpose script menu creator than most command line AND GUI tools, namely due to
- instant previews AND
- the ability to either arrow up and down OR fuzzy type what you want.
For me, it renders, e.g. "select in" and "case" functions in Bash completely obsolete. At any time, if I want quick selection of anything that can be a list, my first thought is, can I do this with fzf?
i was confused how to adjust the width and figured it out, here’s where i asked, learned, and shared my figlet script
fzfont
https://github.com/junegunn/fzf/discussions/3211
https://asciinema.org/a/grALSk2M1tkVPiiyrAGCbdX5J (without width adjustment iirc)
function finstall {
PACKAGE_NAME=$(apt-cache search $1 | fzf | cut --delimiter=" " --fields=1)
if [ "$PACKAGE_NAME" ]; then
echo "Installing $PACKAGE_NAME"
sudo apt install $PACKAGE_NAME
fi
} # ~/.config/fish/functions/fbi.fish
function fbi -a query -d 'Install Brew package via FZF'
set -f PREVIEW 'HOMEBREW_COLOR=1 brew info {}'
set -f PKGS (brew formulae) (brew casks |sed 's|^|homebrew/cask/|')
set -f INSTALL_PKGS (echo $PKGS \
|sed 's/ /\n/g' \
|fzf --multi --preview=$PREVIEW --query=$query --nth=-1 --with-nth=-2.. --delimiter=/)
if test ! -z "$INSTALL_PKGS"
brew install $INSTALL_PKGS
else
echo "Nothing to install…"
end
endI use ctrl-r a lot, though I have no issue remembering exact portions of the commands, but that may just be me.
I'll give it another try.
In the same vein as ripgrep (rg), I recommand the author (and everybody else) to give fd-find (fd) a try as a replacement to find. It's much, much faster (multithreaded search), and has better defaults to use from the shell (doesn't complain about these permissions issues, and `fd abc` is equivalent to `find. -iname 'abc'`.
A few comments here mention it already, but I wanted to recommend it.
I’ve noticed a bunch of CLI tools recently released written in rust that are along this same line of being snappy and well-written. Fclones, ripgrep, paru, fd, exa, to name a few. This probably has more to do with the type of developers the rust platform attracts, rather than the language itself (many awesome tools have been written in go recently as well). But yea, devs who have an interest in Linux and command line tools tend to be great IMO :)
nvm. tutorial explains https://youtu.be/qgG5Jhi_Els?t=400
fzf is also more suited to interactive search, while fd is more of a "give me a list" thing.
If you don't want to install a tool just for this:
fs() { find -iname '*'"$1"'*' ; }
I don't really have big directories where speed would be a benefit, so haven't tried `fd` yet. I do use `ripgrep` but mostly for its features (like default recursive search respecting .gitignore, `-r` option, etc) over speed benefits.time fd --type f 4.61 user 11.27 system 120% cpu 13.172 total Second run: 4.06 user 4.35 system 178% cpu 4.720 total
That's on an SSD. Admittedly, fd skips over hidden files and some directories by default. Adding them back (-u), it seems to take about the same time. 13.74 40.58 145% 37.324
Though it falls back to 13s if I disable colored output.
Piping these in wc -l to eliminate vt overhead, I have 1.4M matches, in about 5s for fd and 11s for find (warm runs, there is quite a bit of jitter). About 1s if I let fd skip hidden directories (.cache, .git, etc: 0.4M matches).
Point is, it's usually noticeably more responsive for realistic use-cases (and has colored output, parses .gitignore files, etc).
https://www.masteringemacs.org/article/fuzzy-finding-emacs-i...
1) list of text 2) input to filter the list down. see: list gets tinier
It's not that you're getting old and it's not that it's "too fast". The problem with many animations like this and why they make no sense whatsoever is because they cycle without adding a little pause... So you have no way to know when it's beginning and when it's ending. It's endemic on Github.
Some .gif files are properly done but IMO most don't explain anything, only add confusion and would be better served by three or four screenshots.
There are poor UX and then there are these kind of cycling .gif files.
An animation that shows a very slow pace might communicate that the tool is slow too.
People's reaction when I showed them was basically WHAT IS THIS MAGIC?!?
The author lists some ways but misses the standard Unix way: simply use locate https://en.m.wikipedia.org/wiki/Locate_(Unix)
Using locate maybe piped to grep is good enough for all my finding needs
Too often do I ssh into some ECS, run a docker or whatever, only to find it doesn't have locate.
The problem then is that even if you can quickly install it, it'll still need to reindex the whole system, which may take minutes (and can easily cause stressed servers to start trashing).
If it ain't there -and it often is- getting it is a lot of hassle.
I'm perfectly satisfied with the default Ctrl+R functionality, to the point that fzf seemed to add visual clutter without any value.
I guess I was never let down by inverse search.
Context: I'm using Linux and doing SWE and SRE work, and constantly live in the terminal.
So, type: ctrl+r, prefix, and then keep pressing ctrl+r to find everything that matches the prefix.
ctrl+r matches any part of the line, not just the commands / start of the line. I'm often using it on a unique argument I remember last using the command with.
It's the fuzziness that really beats ordinary ctrl-r, being able to search on multiple fragments of text is just fantastic.
But I also remember that I can C-r /fzf/ and it will search back to the command:
source /usr/share/doc/fzf/examples/key-bindings.bash
which will instantly upgrade my search capabilities, if I'm stuck. Which doesn't really happen that much.I think the main part of my vanilla C-r experience is that I trained myself to remember commands differently. I somehow remember exact char-to-char tokens, like the "/fzf/" substring above. Or for a more extreme example "ose -p d" when I try to find:
docker compose -p devenv exec myservice /bin/sh
Weirdly, I kinda know that if used "se -p d" instead (shorter) it would land me on a wrong command (so, not shorter).Over time I eventually found myself using Ctrl+Alt+R more and more so I made it the default and now Ctrl+Alt+R is still 'old school' history search for me.
# use up and down arrow to match search history based on typed starting text
"\e[A": history-search-backward
"\e[B": history-search-forward
# if you prefer to search anywhere in the command
"\e[A":history-substring-search-backward
"\e[B":history-substring-search-forward bindsym $mod+d exec --no-startup-id urxvt -title "fzf-menu-random123" -e bash -c 'i3-dmenu-desktop --dmenu=fzf'
for_window [title="fzf-menu-random123"] floating enable function gbll(){
local tags branches target
branches=$(
git --no-pager branch --sort=-committerdate \
--format="%(if)%(HEAD)%(then)%(else)%(if:equals=HEAD)%(refname:strip=3)%(then)%(else)%1B[0;34;1mbranch%09%1B[m%(refname:short)%(end)%(end)" \
| sed '/^$/d') || return
tags=$(
git --no-pager tag | awk '{print "\x1b[35;1mtag\x1b[m\t" $1}') || return
target=$(
(echo "$branches"; echo "$tags") |
fzf --no-hscroll --no-multi -n 2 \
--ansi --preview="git --no-pager log -150 --pretty=format:%s '..{2}'") || return
git checkout $(awk '{print $2}' <<<"$target" )
}
[1]https://github.com/junegunn/fzf/wiki/Examples#githttps://github.com/kbd/setup/blob/master/HOME/.config/git/co...
Perfect example: my "cherry pick" alias:
cp = !git pick-branch | xargs git pick-commits | tac | xargs -t git cherry-pick
Those "pick-" aliases pop up fzf to let me choose a branch, then commits to cherry pick. No more copying hashes anywhere.Similarly, my alias for git add lets me fzf-select a list of files to stage based on what "git status" reports.
1. flog: go to git branches I had checked out in the past quickly
2. pr: preview and check out PRs I've been assigned to review
flog () {
branch="$(
git branch --sort=-committerdate --format="%(committerdate:relative)%09%(refname:short)%09%(subject)" \
| column -ts $'\t' \
| fzf \
| sed 's/.*ago \+\([^ ]*\) .*/\1/'
)"
git co $branch || (
echo -n "git co $branch" | pbcopy
)
}
errcho() {
echo "$@" 1>&2
}
jq_query=$(cat <<- EOF
map(
{ key: "\(.number)"
, value:
{ line: "\(.number):\(if .isDraft then "" else " " end):\(.author.login):\(.title):\(.headRefName)"
, body: "# \(.number): \(.title)
## \(.headRefName)
\(.body)"
}
}
)
| from_entries
EOF
)
pr () {
local gh_user=${1:-}
if [ -z "$gh_user" ]; then
gh_user="@me"
fi
tmpFile=$(mktemp /tmp/prs-XXXXXX.json)
cleanup () {
rm "$tmpFile"
}
trap cleanup EXIT
gh pr list -S "review-requested:${gh_user}" \
--json number,title,headRefName,body,author,isDraft \
| jq -r "$jq_query" > "$tmpFile"
preview_command="\
jq -r '.[\"{1}\"].body' $tmpFile \
| pandoc -f gfm -t markdown \
| glow -s light -
"
pr_number=$(
jq -r \
'to_entries | map(.value.line) | join("
")' \
"$tmpFile" \
| column -t -s: \
| fzf \
--ansi \
--delimiter=' ' \
--preview="$preview_command" \
--preview-window=up:80% \
| grep -o '^[0-9]\+'
)
if [ -n "$pr_number" ]; then
errcho "checking out #$pr_number"
gh pr checkout "$pr_number" && gh pr view --web "$pr_number" && git pull || echo -e "gh pr checkout $pr_number && gh pr view --web $pr_number && git pull"
else
errcho "canceled"
fi
}I whipped up a nice, performant branch picker last night that I'm pretty happy with, hopefully there are some useful tidbits for others. It's similar to your `flog` command but it uses the reflog to find the most recently checked out branches. It filters those which have been deleted using a set structure (well, map of bools), thus requiring BASH 4+. I'll be interested to see the differences in behavior and performance of your approach
https://gist.github.com/pnovotnak/4dfe9b2867bf6fea60fa94b4c8...
https://www.redhat.com/sysadmin/fzf-linux-fuzzy-finder
The tl;dr is that it provides terminal fuzzy finding that can be plugged into just about any task that involves finding things.
I use it many times an hour, it's the main way I navigate files and buffers in nvim, it's how I find files in my projects, it's how I select sessions in tmux.
I'd be lost without it.
fuzz() {
file=$(fzf)
if [ ${#file} -gt 0 ]; then
nvim "$file" # or any editor you prefer
else
:
fi
}I just love how simple it is to stick anything together via the universal plain text interface in the shell and even pipe this text/dataflow through interactive tools like fzf, as you just mentioned.
fuzz() { file=$(fzf) && nvim "$file"; } vim $(fzf)
but don't press enter. Press tab instead. The shell will expand the $(), which will run fzf and let you choose a file. When you've made your choice, fzf will exit, and your choice will be written in place of the $(), so you can see the result before you run it. It works with any command, not just vim. It's particularly useful when doing multi-selects. And if you press ESC then nothing gets written.I've only tested this with zsh, not sure how other shells behave. And you may need to alter fzf's default command or pipe something into it.
Apparently an option_as_alt option should also be introduced in the next release.
Another possibility is to fix it via eg the terminal emulator - but that doesn't work for global hotkeys like window management with yabai via skhd.
See my comment on lobsters: https://lobste.rs/s/nvoikx/helix_notes#c_m8guuh
alias jf="cd \$(sort -nr ~/Library/autojump/autojump.txt | awk 'BEGIN {FS = \"\\t\"} {print \$2}' | fzf)"
or without the escaping: cd $(sort -nr ~/Library/autojump/autojump.txt | awk 'BEGIN {FS = "\t"} {print $2}' | fzf)
It gets my autojump history, sorts it by most used on top, extracts just the directory names, pipes the list to `fzf` and `cd`s into the selection. It's great because my most-used directories will be right on top of the `fzf` list.Zsh's H-S-MW plugin [1], which provides multi word CTRL+R to search the history + a couple of half-assed tools I wrote [2] seem to cover most of the think I would need fzf for, but maybe fzf would work better or be a useful addition to my toolset. Maybe it could replace those half-assed tools.
Of course I intensively use ripgrep.
find . '/'
Was not immediately apparent to me. It will first search the current directory, then search from the root directory of the filesystem. This is useful (beyond just `find` which is implicitly `find .`) because it will turn up local results quickly and global results eventually. find . /
Don't get why there's quoting here.The basic problem, however, is that whatever I type into the fuzzy search, it finds many thousands of hits. It seems it's picking up a lot of aliased Downloads folders in ~/Library/Containers. Anyone else have that problem? Not sure how to turn that off.
I just typed "abcdefghijklmno" trying to narrow it down, and still had 10 hits. Typing a further "p" reduced that to 2, but I could see most of those eliminated 8 had a "p" in the filename. Confusing!
edit: I got Ctrl-R, Esc-C and **TAB complete working by adding this to .bash_profile, not .bashrc as it said to when installing:
source /opt/local/share/fzf/shell/key-bindings.bash
source /opt/local/share/fzf/shell/completion.bash
But I still have many thousands of options for Esc-C cding, for example, whatever I type—mostly from ~/Library/Containers. I don't remember having that problem when I tried fzf a few years ago, on macos 10.13 I think.You just name your folders and subfolders by tags. And then search it via fzf (I prefer the exact search) launched by a hotkey.
To exclude a subfolder from the global search I use a trick. You add the stop symbol " #" at the end of the subfolder name. A custom fzf configuration (see below) then excludes what's the inside. Though, you still can cd into the excluded folder and make fzf search in it. So, in fact it gives you an hierarchy of tagged folders with stop symbols. This helps to avoid search pollution and unnecessary disk reads:
export FZF_DEFAULT_COMMAND="fd -E=**/*#/**"
I also use hotkeys to open items in default programs and to preview files in fzf UI: export FZF_DEFAULT_OPTS="--exact \
--bind 'ctrl-/:execute-silent(xdg-open {} &)' \
--bind 'ctrl-p:toggle-preview'"
Of course, it still requires a strong discipline to tag everything appropriately. But the profit is immense - you can find everything you remember and open it in the default program in a matter of seconds.After pressing <tab> you get sent into fzf to make the decision
This works for ssh too
https://github.com/Aloxaf/fzf-tab
Install this, get an instant improvement to anything you do on the terminal. It works with any existing program with tab completion.
$(brew --prefix)/opt/fzf/install
assuming you've installed fzf with brew.
Something else you can try in your `~/.gitconfig`
[alias]
fza = "!git ls-files -m -o --exclude-standard | fzf -m --print0 | xargs -0 git add"
git fza will quickly let you add files if you've changed a lot.
I alias `git fza` to `ga` and it gets used a lotOh, and another useful shortcut is this:
export FZF_CTRL_T_COMMAND="mdfind -onlyin . -name ."
Obviously only useful for MacOS, but fzf plus spotlight is pretty useful. You can use it to find that lost file that you only remember part of the name![1] https://github.com/phiresky/ripgrep-all
[2] https://github.com/phiresky/ripgrep-all/blob/master/doc/rga-...
https://github.com/kbd/setup/blob/master/HOME/bin/fzr
Basically, prefix any jq command (or rg, or anything you can think of) with fzr and it'll give you an interactive interpreter for your query. People have written a number of dedicated jq interactive repls, but not much is needed beyond fzf.
I also customize it so I can click the folder in the UI and open it in total commander.
alias kctx='kubectl config get-contexts -oname | fzf | xargs kubectl config use-context'
alias prv="gh pr list | fzf | awk -F ' ' '{print $1}' | xargx gh pr view -w"
Amazing stuff. alias prlsf="GH_FORCE_TTY=yes gh pr list | fzf --ansi --header-lines 3 --preview 'GH_FORCE_TTY=yes gh pr view {1}' | awk '{ print \$1}' | xargs -I {} gh pr view --web {}"The intro irks me, though:
> Software engineers are, if not unique, then darn near unique in the ease with which we can create tools to improve our own professional lives; this however can come at a steep cost over time for people who constantly flit back and forth between different tools without investing the time to learn their own kit in depth. As someone with a healthy respect for the tacit knowledge of people better than me, I think a great 80/20 heuristic is “Learn the oldies first”: venerable Unix tools like cat, ls, cd, grep, and cut. (sed and awk, too, if you have the good fortune of landing yourself in an actual modern sysadmin role.)
This seems to be either naive or hubristic in light of the thousands of years of history of craftspeople building their own workspaces. Even a hobbyist woodworker will build plenty of jigs and fixtures for their work. Machinists as well. Anyone whose work comprises building things is readily capable of applying those skills to make their work easier
I think the key line is the ease in which we can create tools. A software engineer has free access to the lumber yard. Builders of old had to work hard to create tools to create tools.
On ease, much of what a woodworker needs can be built from wood and with basic hand tools, so the woodworker, by definition can build many of their own tools.
This is similar to programming, where much of what a programmer needs can be written in code.
On cost, simple observation of woodshops—including many in person and among acquaintances that are not professionals—has shown me that the cost is not prohibitive. Every shop I have seen includes a significant amount of self-built tools and fixtures.
Cost also explains the difference in programming environments I have seen. I have met a great many professional programmers, some of whom have no scripts directory or self-written tools, whose only programming output is their direct work product. This reflects the fact that professional-grade tools are available for free to programmers.
As individuals, my observations lead me to understand woodworkers as much more likely to use their skills to build tools for themselves. The same holds true of other craftspeople I have had the opportunity to observe; even in small, one-off projects, it is common to use elements of the craft to build a tool or otherwise aid the endeavor in a way that is not directly producing the work product.
As a collection, I would agree that programmers build tools that allow us to do our work better. The open source community is incredible. Because of the collective action and the cost to individuals, it is much less common for those individual programmers to have to write tools for themselves.
I would argue that it is similarly easy, but circumstances lead to these disparate outcomes.
While not TDD by the letter, many crafts work more or less agile, and more or less incremental.
An example: I'm currently cutting down some 15 trees (Elms, died of Elm Blight). While it's impossible to cut down a single tree using TDD, I do cut them down incrementally, agile and in small steps: start out with the smallest tree (that stands alone) see how it reacts. Don't plan too far ahead, but plan a little - escape route, sharpen the chains etc. TDD wise: delivery criteria is "wood on a safe, manageable pile".
(I halted the moment a funny little owl peeked out of one of the trees, annoyed. Apparently it was building a nest and I'm too late in the season. Also agile)
And in fact, testing is one of the most widely shared pieces of advice among woodworkers I have seen working and met. If there is more than one of any cut to be made, it is customary to build a custom jig to hold the work, then test on scrap to ensure all is correct. After work pieces are all in or near their final shape, it is typical to do a dry fit: put the entire (or some substantial subassembly of the) finished product together without glue or fasteners to ensure it does fit. Iteration and small tests which allow for fast failure and early correction are a longstanding tradition in woodworking, much longer than in programming.
In my personal experience, working professionally as a programmer and as a hobbyist in woodwork, I have been much more impressed by the degree to which woodworkers build their own tools to support their workflows than by programmers. This holds up among individuals I know personally, discussions I have followed online, and popular personalities I have been exposed to.
If you follow discussions and developments in both communities, it becomes clear that the difference is not the ease with which one can build tools to make their work better, but the price at which programmers can get professional-grade tools for the same purpose.
BSD and Linux are free. Hyper-powered text editors are free. Most programming languages are free. Postgres (and a plethora of other, databases) is free. Compilers, debuggers, package management, CI/cd tools, collaboration tools are all available for free.
As for the availability of raw materials for building such tools to improve work, that is a question of cost, not ease. It is certainly easy to build tools for a woodworker, and the cost is not prohibitive, based on the observation that every woodshop I have seen has a significant amount of tools and fixtures built by the worker.
Use tab to select multiple git branches that are deleted when you hit enter
alias gbD="git for-each-ref --format='%(refname:short)' refs/heads | fzf -m | xargs git branch -D"
Lists git branches and checkouts the selected
alias gcof="git for-each-ref --format='%(refname:short)' refs/heads | fzf | xargs git checkout"
> vi $(find . '/' | fzf): For finding random config files
Instead you can just use:
> vi **<tab>
** is the real gamechanger here. You can use it with ssh, too. I managed about 500 SSH hosts. ssh **<tab> will find hosts via /etc/hosts and .sshconfig
- Combine it with autojump[1] and quickly navigate to any directory you've navigated to before (sorted by frequency). This is crazy helpful.
- Use it for git branch names
- Use it for ssh locations
You have to parse it to extract the directories without all the metadata it spits out.
Then you probably need to write a shell function in your preferred shell to tie the pieces together, as well as assign it to a keybinding. It's not too hard.
I use xonsh for my shell, so my config probably won't help. And doing it in xonsh is probably a lot messier than doing it in Bash.
https://github.com/junegunn/fzf/wiki/examples#autojump
You'll have to comment out the j() function in /usr/share/autojump/autojump.sh
For example:
bundle exec rspec <Ctrl+T>edit: okay done
http://i.imgur.com/n12DYUu.png
Two examples in case you have a preference w/ or w/o the preview:
fzf_insert_directory_preview() {
local dir
dir=$(find . -type d -not -path '*/\.*' | fzf --height 40% --reverse --preview 'tree -C {} | head -200')
if [[ -n $dir ]]; then
READLINE_LINE="${READLINE_LINE}${dir}"
READLINE_POINT=$((${#READLINE_LINE}))
fi
}
fzf_insert_directory() {
local dir
dir=$(find . -type d -not -path '*/\.*' | fzf --height 40% --reverse)
if [[ -n $dir ]]; then
READLINE_LINE="${READLINE_LINE}${dir}"
READLINE_POINT=$((${#READLINE_LINE}))
fi
}
bind -x '"\ey": fzf_insert_directory_preview' # alt-y
bind -x '"\et": fzf_insert_directory' # alt-t
http://ix.io/4rtn/shSent from a LLM
https://github.com/vapniks/fzfrepl: Edit commands/pipelines with fzf, and view live output while editing. There's a cool example using ffmpeg, graph2dot & graph-easy for displaying ffmpeg filter graphs.
https://github.com/vapniks/fzf-tool-launcher: Browse & preview contents of files, and launch tools/pipelines to process selected files. You can use fzfrepl in sequence to process each part of a dataprocessing pipeline with different tools.
> rg . | fzf
How do I do this on windows (NOT Linux)??
*Note:* Assume I already have ripgrep, FZF & VS Code installed.
In fact, I went and tried the other shortcuts and they worked as well e.g. Alt+C
Didn't know about this all this while and I had fzf installed!
Sadly, you don't get the "context" (the content of actual matches) out of the box, you'll have to resort to double-tapping F3 while manually going down the file list. That's a downside, I fully admit that.
In the article, they provide this:
>rg . | fzf | cut -d ":" -f 1
What would be the easy windows equivalent of
> cut -d ":" -f 1
??
I wrote this post following diataxis.fr's advice on writing tutorials:
>A tutorial must help a beginner achieve basic competence with a product, so that they can go on to use the product for their own purposes.
>A tutorial also needs to show the learner that they can be successful with the product - by having them do something both meaningful and attainable.
I think I have succeeded on that front judging by the reaction. :)
For Ctrl+R, how many times are people running similar variants of commands that they 1) don’t know what they typed and 2) didn’t think to simplify their workflow to not be running duplicated commands?
For Alt+C, are peoples’ file and directory layouts such a mess that they need to search entire directory trees to find what they’re looking for?
I’m confused.
Have you ever joined a new project? Usually they're both messy and you don't know where anything is.
Besides, even now with plenty of familiarity in my current legacy code base I can use fzf to type filenames or directories without typing the full path.
Ctrl+R is great for those repeat commands in your shell history that you want to use right now but don't want to add to your .bashrc, like repeatedly running a script with some arguments. I think the value of searching your history is self evident.
Shouldn't your new team members work to help onboard you instead? Shouldn't you get ample training in your job?
For Alt+C, yes files and directories are often a huge mess. It may be no fault of your own. Maybe you're working with a giant legacy codebase or digging into node_modules. Now you can type 'vim Alt+C', to find and immediately open whatever you're looking for.
Of course this can all be done other ways, but it's very convenient and very fast when paired with ripgrep especially.
Also sometimes I want to run something I haven't run in several days.
If you don't, then yes, I use it all the time to find the exact command I typed in a few weeks ago. I'm not going to memorize all the options I passed in, etc.
For Alt+C: Useful even if you organize things very well. I'm in my home directory. I have /home/me/media/video/youtube/channel_name. I want to go in there. That's a lot of typing (even with Tab autocompletion). When I can just press Alt+C and type perhaps 4 characters of the channel name and I'm instantly in that directory. Do this 100 times over for different directories and the benefits become obvious. In the past I would put convenient symlinks to get to deeper directories quickly, and I now realize that approach is just a hack due to a poor navigation interface.
Likewise, if you get your directory layout rationalized, good tab completion and disambiguation feels like all you would need.
Yes, the article misses what fzf actually is by focusing too much on the shell integrations. I hardly ever use those features, except for fzf-tab (which partially subsumes them).
Fundamentally, fzf is a well-behaved UNIX tool for choosing things. It does one thing well (choosing things), it communicates by simple text streams on stdin and stdout, and it integrates seamlessly with other text-based programs.
It's like an interactive, human-friendly version of grep that narrows down the possibilities on every keypress. You pass any newline-separated text to its stdin, and it will let you choose from among them. Whatever you choose will get written to stdout. This can be a single choice or multiple choices. You can customize the layout and even run arbitrary scripts when each option is selected (not chosen) to show a preview.
Once you recognize it, "choosing things" shows up everywhere, so fzf can accelerate any terminal-based workflow. Examples of what I use it for:
- what unit tests to run
- what git branch/commit to check out
- what process to kill
- what wifi to connect to
- what todo list items to check off
- find an emoji and put it on the clipboard
- you're leaving your laptop and you want to choose among shutdown/restart/suspend/logout/lockscreen
- what files or options to pass into an arbitrary command (using fzf-tab)
Basically anything that, if it were in a GUI, would be shown as radio buttons or checkboxes or a dropdown menu.
You can use it in scripts/aliases, or you can just write a quick fzf command inline. I use it for so many things, it's hard to even recall them. It's part of my muscle memory now. Check the wiki on the fzf github, there are all kinds of examples. e.g. here's the one I use for killing processes[2].
An example from recently where I used it "inline": I was in the middle of debugging something in a Python project. I needed to temporarily remove a bunch of packages from the virtual envirnoment, but not all of them, to narrow down where the problem was coming from. After 20 seconds of trial and error (I forgot the syntax for tail) I had:
pip uninstall $(pip list | tail -n+3 | cut -d' ' -f1 | fzf -m)
This let me multi-select from the list of installed packages and uninstall them. Go down the list, boom-boom-boom, done. Pressing enter would uninstall my choices right away. Pressing tab would first expand the $() and replace it with the stdout of the pipeline inside, so the text after the prompt would become `pip uninstall requests numpy pandas ...` or whatever I chose, without running the `pip uninstall` part until I pressed enter. I tend to do the tab-expand trick a lot with multi-selections or with dangerous commands like rm, so I can double-check the full thing first before running it.NB: in that pip example, the "fuzzy" part wasn't even relevant. All I did was use the up- and down- arrows to navigate the list .. there were only a few dozen entries so I didn't need the fuzzy-search. In fact, in most of my scripts I actually turn off the fuzzy matching and use the --exact flag, so that it just searches for exact substrings, whitespace-separated, order ignored. I find this makes its behaviour more predictable. e.g. if I want to find a pyproject.toml file from among all my files, in --exact mode I can just type "pypro" and it will show like
~/repos/foo/pyproject.toml
~/repos/bar/pyproject.toml
~/old-stuff/scripts/pyproject.toml
...
then I type "bar" to narrow it to the one in the "bar" repository, so my query is just "pypro bar". But unlike fuzzy mode, it doesn't show entries that just happen to have "b", "a", and "r" somewhere in the string, like something named ~/repos/big-archives/pyproject.toml
^ ^^
I have to type slightly more than I would with fuzzy-mode, but the lack of bad search results more than makes up for it.This is what I mean when I say the core of fzf is "choosing things". It's not really about fuzzy-searching, despite the name.
It's one of the few packages that I think genuinely enhance fish versus merely adding a bunch of cruft.
alt+ctrl+f and ctrl+r are indispensable.
The only thing that annoys me is working with Android AOSP requires sourcing a bunch of bash functions that I don't feel like porting to fish so I'm occasionally required to drop into bash whereas with zsh and it's POSIX compatibility I could just source the bash functions and it would work fine. But fish's completions work much better out of the box and they have some useful features like being aware of your history in each directory.
Or you could have just typed find / -name nginx.conf
Maybe an extra 2>/dev/null if you dont want to do it from sudo
Or, for this specific example, remember standard config file convention?
Remove-PSReadlineKeyHandler 'Ctrl+r'
Remove-PSReadlineKeyHandler 'Ctrl+t'
Import-Module PSFzf
All other shortcuts worked out of the box!Instead it's :
micro $(fzf) , then type the filename , select it and presee enter to get it opened in the editor.
Which has done wonders for my memory :D
(You _can_ cycle through past entries, but will check this out as list does sound way better + the menus people are describing here sound interesting)
OP here! Apex domains are hard! Sorry, this one works fine now.
For example setting:
export FZF_COMPLETION_TRIGGER='~~'
Will allow fzf completion for a commands. Just need to hit the <TAB> key after ~~ and fzf will pop up:
git diff main..HEAD -- ~~
ls -lh ~~
does not do anything useful on my mac m1
j '' '' '' '' ''
For example, if I know I frequently visit `/users/elahmo/developer/project`, if I type `j pro` I know it will jump there most of the time. If I type `j project`, it will jump 99% of the time. So it is quite good to cycle between things you often open, but of course you can reach things that are in the history it builds.
1. You are running jump for the first time. Have you integrated jump with your shell? Run the following command for help:
$ jump shell
If you have run the integration, enter a few directories in a new shell to
populate the database.
Are you coming from autojump or z? You can import their existing scoring
databases into jump with:
$ jump import
Doesn't work afaik / Tried all doesn't work how do you set this up @elAhmoI have seen other people suggesting https://github.com/wting/autojump too, so it might be worth giving that tool a look, it seems supported a bit better and more actively developed.
Now combine the two: autojump and fzf. Basically look at all the directories in the jump database and pass that to fzf (sorted by frequency). You'll be amazed at what an improvement that is. I've bound it to Alt-j on my shell. I use it a ton more than Alt-c.
First, some env variables for setting defaults:
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'
export FZF_DEFAULT_OPTS='--layout=reverse --inline-info'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
And some neat commands:1. A command for fuzzy jumping between workspaces with tree previews (depends on `tree`):
export WORKSPACE_ROOT="$HOME/whatever-your-root-is"
ws() {
cd "$WORKSPACE_ROOT/`ls -a $WORKSPACE_ROOT | fgrep -v .DS_Store | fzf --preview '(cd $WORKSPACE_ROOT/{1}; pwd; tree -C -L 1 -I node_modules -I .DS_Store)'`"
}
2. A command to be able to fuzzy search files with previews and syntax highlighting. Depends on bat being installed. alias fzp="fzf --preview 'bat --style=numbers --color=always --line-range :500 {}' --border --height='80%'"
3. A command to fuzzily run npm scripts with previews and syntax highlighting. Depends on bat and gojq, but you can sub gojq with jq. It does have a bug where it doesn't handle ":" characters in script keys well, but I'll fix that at some point. npz() {
local script
script=$(cat package.json | gojq -r '.scripts | keys[] ' | fzf --preview 'cat package.json | gojq -r ".scripts | .$(echo {1})" | bat -l sh --color always --file-name "npm run $(echo {1})" | sed "s/File: /Command: /"') && npm run $(echo "$script")
}
4. A command for rapidly selecting an AWS profile. As a user of AWS SSO with many accounts/roles, this is a life-saver. I combine this with having my prompt show the currently selected role for added value. alias aws-profile='export AWS_PROFILE=$(sed -n "s/\[profile \(.*\)\]/\1/gp" ~/.aws/config | fzf)'
alias ap="aws-profile"
5. A command for fuzzily finding and tailing an AWS cloudwatch log group. I didn't come up with this one, and I can't remember where I read about it, or I'd attribute it properly. awslogs() {
export AWS_PROFILE=$(cat ~/.aws/config | awk '/^\[profile /{print $2}' | tr -d ']' | fzf)
local log_group=$(aws logs describe-log-groups | gojq -r '.logGroups[].logGroupName' | fzf)
aws logs tail "$log_group" --since 3h --follow --format=short
}
I also have a few other commands related using fzf, but they're more bespoke and probably not useful to others.It tries to solve the "now what" part.
export FZF_DEFAULT_OPTS="--exact"
Or invoke it with one of these: fzf --exact
fzf -e
Or if you start your search with a single quote ' it disables fuzz. (But maybe you already knew that, it sounds like you want the first option.)I find myself getting tripped up even with zsh missing.
nvim $(rg . --vimgrep | fzf | awk -F: '{print $1, "+" $2+0}')
So you don't need to do stuff like
vi $(find . '/' | fzf)
And instead can use fzf from inside the editor.It also supports integrating it with fdfind and ripgrep.
For when you neither rememeber exactly
ctrl + r for most things
history | grep for searching when i forget the exact command or want to see related commands.
I don't want pull downs, pull ups, drop downs, expanded hamburger menu, hot dogs, another set of key strokes to remember how i started some service last month.
You seem to be stuck in your ways, so that even when new better alternatives are created, you dismiss them unceremoniously, possibly without having taken the time to evaluate them.
It's something I've noticed with quite a few linux users, and I find that super interesting!
May I ask what's your age range? (just curious)
Do you think the time spend in an exploratory phase for new tools (ex: getting familiar with fzy) would not be recovered by time gains in the exploitation phase? Or is it because of a fear of the unknown, or a belief that old habits may be hard to change?
It's a very serious question BTW, I hope it's not too personal
vi $(fzf)
He claims to save a bunch of keystrokes by using it this way. But the string literal `vi $(fzf)` is saved in ~/.bash_history, not the actual filename. So you have to memorize the filename, AND deal with all those hamburgers and hot dogs, if you ever wanna re-edit that file after closing it.My issue is that at work, I tend to work with a lot of projects and repos, and I work a whole lot in a couple repos. And honestly, even if its short, typing `cd ~/Pro<tab>/an<tab>/main_<tab>` becomes old after 2000 times or more. And then there are ~80 - 90 repos in ~/Projects/terraform, and a couple dozen legacy repos in ~/Projects/chef, a dozen different config repos in ~/Projects/config, an automatically rotating scratchpad in ~/Stuff, ... I don't know if it's a messy workstation, or I'm just dealing with a boatload of stuff.
That's why I wrote goto. goto works in 2ish phases. The first phase just uses fzf -1 to give me a selection of the broad strokes I tend to navigate to. In case there is a prettier way to make fzf give me a static list of choices, let me know. It's basically:
DESTINATION=$( echo "ansible\nterraform\nchef\nconfigs\n..." | fzf -1 "$1" )
This way I can either launch goto to get a list of 6-7 things I can select with arrows, by typing, or I can just be like `goto tf` and it directly selects `terraform`.After that, it's a big switch-case selecting where to `cd` to, as well as possibly doing some normal init things. `ansible` as a destination for example just CDs and that's it.
`terraform` on the other hand does a bit more:
case terraform)
DESTINATION2=$( ls ~/Projects/terraform | fzf -1 "$2" )
cd "$DESTINATION2"
git pull --rebase
# more stuff
;;
This way I can either say `goto`, select `terraform`, select a project and get moved there. Or I go `goto terraform` and type `prj2 foo` and usually fzf gives me the right thing, or I can be like `goto tf 'p2 fo'` and it drops me into the right place. Or something like `goto config infra-logaggregation` (or rather, `goto c ilg`). Or `goto stuff ticket-id` to get into some older scratchpad.I'll just have to consider if I want to stick with ohmyzshs alias of g == git, which I don't really use much. Then I could achieve the entirely readable `g c ilg` to go where I need to, hah.
All of this is then wrapped into an alias, which basically aliases `goto` to be `source ~/goto`.
IDK, thanks for listening to my TED-talk on how to use fzf to juggle way too many hats at once. It's just saved me a lot of keystrokes getting around and mashing incomprehensible spells into the shell so coworkers are confused is always fun.
I use this all the time when I'm running unit tests from the command line for instance. Helps me quickly get the path to the file I want without needing to remember the details of the folder structure. Just Ctrl+T -> tests/GLPage will yield me tests/long_path/GoddamnLoginPageTests.foo