bashscriptingshellmenuinteractive

Best(?) way to make a popup menu for semi-portable shell scripts?


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:

...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.


Solution

  • 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.