Create menus and dialogs for shell scripts

Slashdot it! Delicious Share on Facebook Tweet! Digg!

Forms

If you need not only simple forms but also a complex input box, dialog provides you with the necessary tools. You can navigate in the forms using the tab and arrow keys. The syntax might seem confusing at first, in that there are size and position adjustments to make.

Listing 9 is a script that allows editing of data and shows how to return it as variables that can stored permanently in a file. Figure 5 shows how the position and size values are rendered.

Listing 9

Return Data as Variables

#! /bin/sh
a="Mr."
b="Tux"
c="Penguin"
dialog --title "Title" --backtitle "Customer Data" --ok-label "Save" \
  --stdout --form "Catalog" 10 60 3 "Salutation  " 1 1 "$a" 1 15 30 0 \
  "Family Name " 2 1 "$b" 2 15 30 0 "First Name  " 3 1 "$c" 3 15 30 0 > output.txt
a=$(cat output.txt | head -1)
b=$(cat output.txt | head -2 | tail -1)
c=$(cat output.txt | head -3 | tail -1)
rm output.txt
dialog --title "Title" --backtitle "Background Title" --msgbox \
  "Saved values: \n $a \n $b \n $c " 0 0
Figure 5: Position and size values for form elements.

To ensure that redirecting output to a file works, you can precede --form with the additional --stdout option. In msgbox , use the newline character (\n ) to output each variable value on a separate line.

Forms with the inputmenu function need fewer size and position parameters, but you may not get the data of all the fields at the end. You can change only single fields with each dialog call. The output includes the action (RENAMED ), field identifier and data. To be sure the function works, use the --stdout option, as you do for --inputmenu . Post-processing of data occurs with cut (Listing 10). Here, it's important that the field names do not have space characters. Databases can provide the corresponding field names so that it's easy to provide SQL statements with matching variables.

Listing 10

Post-Processing of Data

#! /bin/sh
newval=$(dialog --title "Title" --backtitle "Background Title" --stdout --inputmenu "MENU HEADING" \
  17 60 15 "row-1 >" "Value 1" "row-2 >" "Value 2" "row-3 >" "")
column=$(echo $newval | cut -d \> -f 1 | cut -b 9-)
entry=$(echo $newval | cut -d \> -f 2 | cut -b 2-)
dialog --title "Title" --backtitle "Background Title" --msgbox "Stored Values: \n $newval \n $column \n $entry " 0 0
dialog --title "Title" --backtitle "Background Title" --msgbox "Saved values: \n $a \n $b \n $c " 0 0

The --calendar function provides an easy way to input date information. Calling it displays the current month and the day of the week in the left column. Use the cursor and up/down arrows to move to other data. Clicking the OK button returns the highlighted value.

To fill a variable, again use the --stdout option for output. Because dialog separates the data parts with a slash character, tr translates them into commas. Listing 11 shows how to construct a script that does just that.

Listing 11

Translate Data with tr

#! /bin/sh
a=$(dialog --title "Title" --backtitle "Background Title" --stdout --calendar "TEXT" 0 0 | tr \/ .)
dialog --title "Title" --backtitle "Background Title" --msgbox "Selected date: $a " 0 0

You enter time values with --timebox . To accept the entered value, again use --stdout . The window shows the time the call to the function was made. To enter different values, use the arrow key to proceed to the next entry field and provide another date.

You can enter any alphanumeric values using the input box (--inputbox ). Register a value in a script by using the prefix --stdout option. You can provide an editable default value. The process requires only one line:

a=$(dialog --title "Title" \
  --backtitle "Background Title" \
  --stdout --inputbox "HEADLINE" \
  0 0 "DEFAULT")

You can edit small text files in a mini-editor (--editbox ). Give the filename as an argument. If no file exists, create one with touch .

You can write enter or modify text and then save to another file or overwrite the original file. Specify a height and width for the widget so that the inner window doesn't displace the headline. Again, use --stdout so that the entries don't end up in the standard error output. This function again requires a simple line of shell code, as follows:

dialog --title "Title" --backtitle \
  "Background Title" --stdout \
  --ok-label "Save" --editbox text.txt \
  20 75 > new.txt

You can select directories and filenames from within the script with --dselect and --fselect , respectively.

These two parameters are usually used together. It's usually a good idea to prompt users unfamiliar with the context. You select a directory at the same level as the default with arrow keys and pressing the spacebar.

Choose the path on the left with --fselect ; press the spacebar twice and choose the file on the right. Navigate between fields with the tab key and inside fields with the arrow keys.

The bottom field allows for manual entries. With the cursor in it, move to the next highest level by deleting everything up to the right-most slash (Figure 6). You can see how to select a directory and file in Listing 12.

Figure 6: The --dselect and --fselect widgets make choosing directories and files easy.

Listing 12

Select a Directory and File

#!/bin/sh
a=$(echo $HOME)
while true
do
  a=$(dialog --title "Title" --backtitle "Background Title" --stdout --fselect $a 0 0)
  dialog --title "Title" --backtitle "Background Title" --defaultno --yesno "$a Apply"  0 0
  if [ $? -eq 0 ]; then
    break
  fi
done

You can provide multiple selections using --checklist . This option returns the designator of each entry enclosed in quotes. Field separators are space characters. You give each entry a status of on for marked or off for unmarked. You can specify space-separated values for height, width, and list height.

The list height should correspond to the actual number of items. If all the items don't fit, the box is scrollable.

Listing 13 shows the code together with tr , which removes the quotes. You can build a single-selection box in the same way by using the --radiolist dialog type and setting a single entry to on , getting the tag value without the quotes. Use the space bar to choose the value:

a=$(dialog --title "Title" \
  --backtitle "Background Title" --stdout \
  --radiolist "SELECTION HEADING" \
  10 40 3 TAG-1 "INFO-1" on \
  TAG-2 "INFO-2" off TAG-3 "INFO-3" off)

Listing 13

Provide Multiple Selections

#!/bin/sh
a=$(dialog --title "Title" --backtitle "Background Title" --stdout \
           --checklist "SELECTION HEADING" 10 40 3 TAG-1 "INFO-1" on \
           TAG-2 "INFO-2" off TAG-3 "INFO-3" on)
a=$(echo $a | tr -d \")
sleep 5

This changes the item selection to a single one. The script creates the data query dynamically. The tag consists of the record value or a unique value that the script uses to read in the record value.

The example in Listing 14 shows an SQL query with the psql client for the PostgreSQL database. The aim is to pass the customer number orgnr to other parts of the script. The script writes the instructions for the menu with --radiolist into a variable and executes the code with eval . For further development, the --form or --inputmenu dialog type can be used depending on programming preference.

Listing 14

Sample SQL Query

#!/bin/sh
# Search dialog for the database query
sube=$(dialog --title "Customer Search" --backtitle "Customer Management" \
  --stdout --inputbox "Enter customer name" 0 0 "")
# Check for sufficient data
a=$(psql -t -c "select orgnr from customers where name = '$sube';")
if [ -z "$a" ]; then
  dialog --title "Customer Search" --backtitle "Customer Management" \
         --msgbox "No matching customers found! " 0 0
  exit  # or break when in loop!
fi
# Get data for single-selection and select structure
# Counters for first "on" status
v=0
teil1=$(echo "a=\`dialog --title \"Customer Search\" --backtitle \"Customer Management\" \
  --stdout --radiolist \"found: \" 10 40 3 ")
# Determine customer numbers
for i in $(psql -t -c "select orgnr from customers where name = '$sube' \
  order by lastname, firstname, birthday ;"); do
  if [ $v -eq 0 ]; then
    status="on"
    v=1
  else
    status="off"
  fi
  info=$(psql -t -c "select (lastname || , , || firstname || , , || birthday ) \
  from customers where orgnr = $i;";)
  part2=`echo $part2 $i \"$info\" $status`
done
selection_mask=$(echo "$part1 $part2\`")
echo $selection_mask
eval $selection_mask
dialog --title "Customer Search" --backtitle "Customer Management" \
  --msgbox "Record number $a selected " 0 0

You can add menus to your program with the --menu dialog type. It provides a tag that you later evaluate. As with almost all other value output, use --stdout before the dialog type. Listing 15 shows an example.

Listing 15

Add Menus

#!/bin/sh
while true; do
  a=`dialog --title "TITLE" --backtitle "BACKGROUND TITLE" \
    --stdout --menu "MENU HEADING" 0 0 0 1 "FIRST" 2 "SECOND" 9 "END"`
  if [ $a -eq 1 ]; then
    dialog --title "Title" --backtitle "Background Title" \
    --msgbox "First entry selected" 0 0
  elif [ $a -eq 2 ]; then
    dialog --title "Title" --backtitle "Background Title" \
    --msgbox "Second entry selected" 0 0
  elif [ $a -eq 9 ]; then
    dialog --title "Title" --backtitle "Background Title" \
    --no-label "End of program" --yes-label "Continue" \
      --yesno "End of program?" 0 0
    if [ $? -eq 1 ]; then
      break # or exit
    fi
  fi
done
dialog --title "Title" --backtitle "Background Title" \
  --msgbox "Saved values: \n $a \n $b \n $c " 0 0

Conclusion

If you want to enhance your shell scripts with a user-friendly interface, you'll find everything you need with the dialog tool.

If you require simple queries only, you can benefit from prefabricated building blocks. Otherwise, you can always use something other than shell code.

Buy this article as PDF

Express-Checkout as PDF

Pages: 8

Price $0.99
(incl. VAT)

Buy Ubuntu User

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content