I started making myself simple CLI menus about a year ago, and now I don’t know how I lived without
it. Essentially my menu
command takes a list of inputs, and allows you to select one, and return
that value. Seems too simple to be useful, right? Let’s see the real world example:
$ ls
Dockerfile Rakefile Gemfile Gemfile.lock
$ echo "You selected: $(ls | menu)"
1) Dockerfile
2) Rakefile
3) Gemfile
4) Gemfile.lock
#? 3
You selected: Gemfile
Ok, but what good is that?! Let’s say you want to edit a specific file but you don’t remember the name:
$ vi $(find ./ | grep -i controller | menu)
1) app/controllers/home_controller.rb
2) app/controllers/awesome_controller.rb
#?
One last modification: If only one item is found, just return it.
$ echo "You selected: $(find ./ | grep -i awesome | menu)"
You selected: app/controllers/awesome_controller.rb
Now, I use two flavors of my menu
command. If fzf
(Fuzzy Finder) is installed, it uses that,
and if not it falls back to bash’s select
command. fzf
has the advantage of being able to type
partial file names, and it allows multi-select which I leave on by default, so I rather recommend
giving it a chance, even if it’s not available on every machine.
Here is my menu
command at the time of writing:
#!/usr/bin/env bash
if [[ -x "$(which fzf)" ]]; then
exec fzf -0 -1 -m --bind ctrl-a:select-all
else
in=$(</dev/stdin)
if [[ -z "$in" ]]; then
echo "menu - nothing to display"
exit 1
fi
readarray lines <<< $in
if [[ "${#lines[@]}" == "1" ]]; then
echo "${lines[0]}"
else
IFS=$'\n'
select sel in ${lines[@]}; do
echo "$sel"
exit
done < /dev/tty
fi
fi