Basically I would love to say:
echo `grep ^foo /usr/share/dict/words | popup_menu`
...and have some type of keyboard navigable menu popup or selection tool, very similar to how vim's ":Explore" mechanism works.
Extreme bonus points for "easy and works pretty much everywhere with standard tools"
Also acceptable is "needs some sort of extra config file or 5-10 line shell script"
Less acceptable is "go download this perl library or 100 line python script, etc..." at that point, I would rather just try to find some actual program / package to install and list it as a hard dependency. But if you can come up with a 2-5 line perl / python script that doesn't require tracking down libraries that'd probably work too.
I have investigated:
Dialog - appears more geared towards "shell application" instead of ad-hoc scripting (looks like there might be a way to make it do what I want, though), drawback is that it overwrites the current screen state
Curses - seems like it targets "C" or would need to be used as part of a perl / python library, would have to write my own menu program using this
bash "select" builtin - works via number selection, not keyboard navigation, is a little awkward to use but fairly close
Vim - "grep ^foo /usr/share/dict/words | vim -" ... this gets you surprisingly close, just missing "bind the enter key to print current line to terminal and exit"
...so, how do I go about making or finding a decent, simple, ad-hoc menu maker for use in bash scripts and when I'm being lazy on the command line?
... git checkout -b `git branch -a | menu`
... ssh `grep foo /etc/hosts | menu`
... rm `ls | menu` # ignore obvious quoting issues with this...
Edit: thanks for the answers so far, but want to re-emphasize that I'm looking for ASCII / text menus (not xwindows). I'm trying a few things out locally but nothing is hitting the sweet spot yet.
After thorough investigation, the winner of best(?) way to make a popup menu is as follows:
select f in aaa bbb ccc ddd ; do echo $f ; break ; done
It isn't actually a popup menu per-se but you get the best bang for your buck as far as using standard unix-isms and it is pretty much universally available. Wrapping it in a simple shell script is easy to do wherever you are and means you can reliably integrate its benefits into your workflow.
$ cat ~/bin/menu.sh
#!/bin/sh
ALL=`cat`
select FOO in $ALL ; do echo $FOO ; break ; done
$ ls /usr | ~/bin/menu.sh
1) bin 3) include 5) lib64 7) sbin 9) src
2) games 4) lib 6) local 8) share
#? 2
games
(EDIT: 2024-02-27) In actuality though, you want to use the "select f in ..." idiom as a fallback for when the ~dialog~ fzf
command isn't available. I also have an fzf-select
which adds --multi
to fzf
(multi-select using tab
, as per fzf
docs).
$ cat ~/bin/fzf-menu
#!/bin/bash
SELECTED="$( cat | fzf --select-1 --query "$1" )"
if [[ "0" == "$?" ]]; then
echo "$SELECTED"
else
echo 1>&2 'INFO: Selection Cancelled!'
exit 1
fi
For posterity, the original dialog
script follows. It's kindof ugly but gets the job done as far as providing the same inputs and outputs as above but with a more comfortable user interface.
$ cat ~/bin/gui-menu.sh
#!/bin/sh
# get stdin
ALL=`cat`
# number the lines
SPLITTED=$( echo $ALL | sed 's/ /\n/g' | awk -- '{print NR, $0 }' )
# prompt via dialog (output-fd=1 is so that dialog gui doesn't go to subshell)
OUT=$( dialog --output-fd 1 --ok-label Select --menu Choose 0 50 22 $SPLITTED )
EXIT_CODE=$?
# handle escape / cancel buttons
if [ "1" = "$EXIT_CODE" ] ; then exit 1 ; fi
if [ "255" = "$EXIT_CODE" ] ; then exit 1 ; fi
# extract text corresponding to user's numeric selection
CHOSEN=$( echo $ALL | sed 's/ /\n/g' | awk -- "NR==$OUT {print \$0 }" )
# print result
echo $CHOSEN
And third place goes to Joey Hess's "vipe" interactive pipeline editor (from "moreutils" package), which lets you edit a pipeline and pass its output back out.
echo `ls | vipe`
The above command isn't quite a dialog box (can't just use up / down arrows and press enter, actually have to delete all the lines you don't want) but it is useful because it handles both interactive single and multi-select use cases and is just an all around interesting tool.
For GUI selection, zenity as referenced by Jack looks like a winner as far as ease of use compared to dialog ... dialog unfortunately doesn't "ad-hoc" very well but combining dialog with a "select f in ..." fallback is what best matches my needs.