views:

70

answers:

4

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.

+1  A: 

zenity is very simple to use in scripts but it creates GUI windows. Not sure if it applicable for you...

Jack
zenity is cool...grep ^foo /usr/share/dict/words | zenity --list --column=PICK-A-WORDGood:* easy to use integrate with a shell pipeline* works fine with mouse-less operation (window comes up focused, up/down/enter keys work and dismiss as expected)Bad:* not on OSX by default (I spend a lot of time in OSX shells)* is fundamentally a gui program which means it's awkward over an ssh session
+1  A: 

You can use the --keep-tite option to dialog if your version has it and your terminal supports alternate screens. For example, xterm and some xterm-compatible terminals do.

With this option, output is switched to the alternate screen, the dialog is displayed and when it's closed, the screen switches back showing the previous contents. This is the way vim works, for example.

The dialog will still completely occupy the screen however.

The "ti" and "te" in "keep-tite" represent the termcap codes that are used to bracket a program that uses cursor motion. The corresponding terminfo codes are smcup and rmcup. See man 5 termcap, man 5 terminfo and man xterm (Other Features).

You could also do it yourself something like this:

tput smcup
# bash select menu
tput rmcup
Dennis Williamson
A: 

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

In actuality though, you want to use the "select f in ..." idiom as a fallback for when the dialog command isn't available. The following shell / dialog script is 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

...it is used exactly as the above "menu.sh" but prompts a gui instead of numerically. It's relatively easy to expand the above to allow dialog multiple checkboxes (very inefficiently, probably n^2-ish in the below implementation), which is show here:

    $ cat ~/bin/gui-multiselect.sh
    #!/bin/sh

    # get stdin
    ALL=`cat`

    # number the lines
    SPLITTED=$( echo $ALL | sed 's/ /\n/g' | awk -- '{print NR, $0, 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 --separate-output --checklist 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

    # loop through selected numbers
    for X in $OUT ; do
        # inefficiently print out the text corresponding to the selections
        CHOSEN=$( echo $ALL | sed 's/ /\n/g' | awk -- "NR==$X {print \$0 }" )
        echo $CHOSEN
    done;

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.

A: 

If you know that your script will run on a specific distribution of linux then you can use the programs they've already developed for doing notification popups. I was looking for the same thing here: linux command line popup

Jay
zenity is closest / best for this when using graphics, but I'm looking for terminal, ascii, ssh / shell.
The picture is optional, you can still use them for a text only pop up.
Jay