2010/09/30

Bash and dialog

First, I rant about dialog: WHAT THE HELL WERE THEY ON WHEN THEY WROTE THIS THING?! First, they make keyboard inteaction completely different from what normal users would expect. And then they make it very very hard to actually get the results into a shell script. What's more, every time I write a new script that uses dialog, I realise it lacks many features I need. So I have my own forked / hacked version that I deploy. Thank God for Open Source Code.

Second, I give you some code snippets that will show you how to do simple things with dialog:

Say you want a menulist and want to know which one was selected:
resp="$( dialog ..... 2>&1 >/dev/tty)"

Here we assign a value to the variable resp. The "" preserves whitespace. The $() causes the value to come from a sub command. dialog ... is the "normal" dialog invocation. 2>&1 causes stderr to be sent to sdtout, so that it ends up on resp. >/dev/tty causes dialog's stdout (its original stdout, that is, not the bits of stdout that are coming from stderr) to go straight to the controling TTY, rather then ending up in resp, which would be silly.

Now, say you had a shell function call do_something. This function sets serveral variables as a side effect and takes a while doing so. Maybe you'd want to use dialog's --progressbox or --gauge so the user has something nice to look at while work is going on. If so you might try
do_something | dialog --progressbox

And you would quickly encounter failure; do_something ends up in a sub-shell, whence it can not set the variables in the main shell.

So, you spend time with the BashFAQ and on #bash complaining trying to find an answer. It turns out that FIFOs (aka named pipes) are the only way:
fifo=$(mktemp -u)

mkfifo $fifo
trap "rm -f $fifo" EXIT
dialog --progressbox "Wait" 12 40 <$fifo &
pid=$!
do_something > $fifo
rm -f $fifo
wait $pid

So, we create a randomly named FIFO. Run dialog in the background, reading from the FIFO. Run our function in the foreground, stdout going to the FIFO. When do_something is done, we remove the FIFO and wait for dialog to exit. Which it should, after the fifo has been removed. The trap makes sure the FIFO is deleted even if we exit early.

If you ask me, there's got be a better way, one that doesn't involve mkfifo and does involve exec. But I can't get my head around exec.

EDIT: turns out there is a better way:
do_something > >(dialog .....)

No comments: