Fuzzy finding improved

So sometimes something on the command line is not exactly the way I want it. Sometimes it gives me that itch that won't go away and whispers in the back of my mind, "fix it", "fix it now", "fix it now or the world will end". So I took a bit of time to make fzf work the way I wanted it to today.

  • First itch, "No way to control recursion depth."
    • Which is fine given fzf's design choices. But sometimes I just need to be able to find a file in a specific directory and the sub-directories are less important or sometimes might contain similar but wrong files.

Leverage the well designed fzf app with *nix magic.

Luckily shell aliases can contain all sorts of complex logic and chained apps to get what you want, in this case:

 alias fz="find . -maxdepth 1 | sed 's/^\.\///g' | fzf"

And tada! Now we can just use fz to search the current directory thanks to the -maxdepth argument. The sed one liner strips the leading ./ that find includes in the output stream for a cleaner experience.

Combine that with the bat as a viewer to preview our files and we're starting to get somewhere.

 alias fz="find . -maxdepth 1 | sed 's/^\.\///g' | fzf --preview 'bat --color=always --style=numbers --line-range=:500 {}'"
  • Second itch, "Directories don't show anything useful in the preview."
    • So we don't want endless recursion but having an idea of what is in each directory on our current file level via the preview would be useful.

      In fact the tree output set to a depth limit would be perfect.

So bat doesn't include such conditional logic, but that's fine as it's built to do one thing and do it well. And the preview is just a command to execute and show the output, so we can really put anything in there. So I started a script to do the preview that opens the input and makes decisions on how to output on it.

opener.bash

#!/usr/bin/env bash

if [ -d "$1" ]; then
    tree -a -C -L 3 "$1"
else
    bat --color=always --style=numbers --line-range=:500 {} "$1"
fi

So just chmod a+x on the script and symlink that script into ~/.local/bin which is in my $PATH (essentially installing it). I usually drop the extension from command line scripts for convenience so the symlinking command looks like this:

ln -s $HOME/code/opener/opener.bash $HOME/.local/bin/opener

Then we update the alias to use the script, which ends up being much cleaner:

alias fz="find . -maxdepth 1 | sed 's/^\.\///g' | fzf --preview 'opener {}'

And bam! Now we can see text files and directories in the preview pane of fzf.

  • Third itch, "Binary files show me nothing useful in the preview."
    • Ok, so normally I'd use a hex editor to get some idea of what is in a binary before jumping to something like Ghidra to really see what's going on. Usually there are some important clues in the very beginning of the binary that could be useful.

So I can include a test to try and determine if a file is a binary in the opener script. The file command has some magic to help with this. And there is a nifty Rust crate called hexyl that I haven't had an opportunity to use yet that will let me read just first 4kB of a binary and display it in a wonderous rainbow of colors. So this is how it shook out:

#!/usr/bin/env bash

if [ -d "$1" ]; then
        tree -a -C -L 3 "$1"
else
        BAT_TEST=$(file --mime "$1")
        if [[ $BAT_TEST == *binary ]] ; then
                hexyl -n 4kB "$1"
        else
                bat --color=always --style=numbers --line-range=:500 "$1"
        fi
fi

The binary test using file gets the mime output, which conveniently ends in "binary" if it thinks the file is a binary, so we can store that in the BAT_TEST variable and test for it with the conditional and run hexyl or else run bat.

Short, simple, fast and maybe even useful in more ways than I originally intended. Now the preview script shows something useful for every file it highlights and I have enough focus to narrow down to just where I am working. So in some ways this is a replacement for ls (which is a bit strange as I already have 2 of those). But it satisfies my desire for a tool that is more forensic, gives a lot more information up front and can reveal clues to what is going on in whatever file store I happen to be looking in.

Itch scratched.

Code and instructions are here: opener