Archived content Archive date: 2019-06-25 This content is no longer being updated or maintained. The content is provided “as is.” Given the rapid evolution of technology, some content, steps, or illustrations may have changed. This content is no longer being updated or maintained. The content is provided “as is.” Given the rapid evolution of technology, some content, steps, or illustrations may have changed.

If you walk into any crowded machine room, chances are you’ll catch chit-chat about “shebangs,” slashes, dot-dots, root, pipes, ports, and dash-dash this and that. If you speak UNIX®, you’ll no doubt grok the local lingo — acronyms, command names, shortcuts, options, file names, and colloquialisms about UNIX — and feel right at home. Like practitioners of other art, UNIX users have an extensive vernacular for describing the specifics of their work.

Frequently used acronyms GUI : Graphical user interface

: Graphical user interface HTML: Hypertext Markup Language

But not everyone speaks UNIX; in fact, some may find the command line daunting or perplexing. Further, you simply may not want to entrust the entirety of the command line to an occasional or inexperienced user. To assist those unaccustomed to the command line or to build custom solutions around the shell, you can build GUIs for your scripts. With such tools — dialog and Zenity are two worthy of mention (see Related topics) — you can use dialog boxes, file browsers, and other common “windowing” controls and techniques to interact with your users. Indeed, dialog boxes provide for more natural conversations: You present information, ask for a response, and react accordingly.

This installment of “Speaking UNIX” looks at dialog and Zenity and shows how you can turn any script into a convincing GUI application. You use dialog with traditional, text-based interfaces; Zenity proffers the style of the modern, windowed desktop.

Add dialog boxes to any shell script

A command-line utility typically offers sufficient options to completely control each invocation. Some switches may enable or disable a feature, while other switches may process arguments, such as a list of names. On the command line, you present (nearly) all the information up front and let the job go. Graphical applications are very different. Choices are made through menus, check boxes, and file browsers. A graphical application takes in a little information, processes it, and then usually asks for more information. It’s said that GUI applications are event driven.

The dialog utility spans the two worlds. You invoke the utility whenever you need input from the user, and then return to your script to continue processing whatever data was provided. In other words, if you write a script to use dialog , you’ll likely ignore command-line arguments and instead use dialog to prompt for information when necessary.

If your system lacks the dialog utility, you can easily install it with your distribution’s own package manager, or you can build it directly from source. For example, if your system uses Aptitude, you can install dialog with the command:

sudo apt-get install dialog Show more Show more icon

Otherwise, to build from source, download the code from maintainer Thomas Dickey’s Web site (see Related topics) and run the typical trio of commands: ./configure && make && make install :

wget http://invisible-island.net/datafiles/release/dialog.tar.gz tar xzf dialog.tar.gz cd dialog-1.1-20100428 ./configure make sudo make install Show more Show more icon

After the installation, you should have a new utility named dialog in your path. Type man dialog to see the bundled documentation.

Using dialog is simple: It’s just another UNIX command. You display a dialog box of your choice using the command’s options, then capture the result and perform some logic based on that value. Some variations of dialog place the result of the command directly in the special shell status variable, $? , which you should save or interrogate immediately after the dialog command exits (because a subsequent command will immediately change its value). Other, typically more complicated variations of the dialog command both set the shell status variable and generate other results. To make things simple, dialog provides the --stdout option to always emit its result to standard output, making it easy to capture data with command evaluation (a combination of a command in back quotes and an assignment statement).

For example, the command dialog --yesno is one of the simplest variants. It presents a question, prompts for either a yes or no response, and returns either 0 or 1 in $? depending on whether the user selected “Yes” or “No,” respectively. You can test the value of $? and execute some conditional code. Here’s is a working snippet you can add to a shell script:

dialog --yesno "Do you want to continue?" 0 0 rc=$? if [ "${rc}" == "0" ]; then echo Yes else echo No fi Show more Show more icon

The --yesno option requires at least three arguments: text for the question and the height and width of the dialog box itself, measured in rows and columns. If you don’t require specific dimensions, you can always use 0 for either height or width to size the dialog box automatically. (There are also options for placing the window relative to the top left corner of the window.) Figure 1 shows --yesno in operation.

Figure 1. The –yesno operation



The dialog option --calendar presents a calendar to allow the user to choose a specific date. If the user chooses a date, and then clicks OK, the command returns 0 . If, however, the user clicks Cancel, the command returns 1 . Moreover, if the user clicks OK, the command emits the date selected to standard output. Here’s an example using command evaluation to yield a date:

RESULT=`dialog --stdout --title "CALENDAR" --calendar "Please choose a date..." 0 0 9 1 2010` retval=$? Show more Show more icon

The --title option uses the next argument to add a title to the dialog box and can be used with any dialog command. Much like --yesno , you provide some text to prompt the user. Next, the options 0 0 again specify automatic height and width, and the options 9 1 2010 dictate the initial day, month, and year, respectively, shown in the calendar. The Tab and arrow keys alter the calendar and choose a date. After the dialog box is dismissed, if retval is 0 , the value of RESULT is the date selected. Figure 2 shows the calendar dialog box.

Figure 2. The calendar dialog box

The dialog command offers most of the controls typically found in a graphical application:

--infobox simply presents information: It does not expect any input. The information box remains on screen only briefly. To prolong its display, place a sleep command between it and the next command.

simply presents information: It does not expect any input. The information box remains on screen only briefly. To prolong its display, place a command between it and the next command. --input collects a single, typed response. You might use this command to collect your user’s name or zip code.

collects a single, typed response. You might use this command to collect your user’s name or zip code. --textbox displays the contents of a text file. If the file exceeds the vertical height of the dialog box, a control allows for simple scrolling up and down.

displays the contents of a text file. If the file exceeds the vertical height of the dialog box, a control allows for simple scrolling up and down. --menu and --radiolist present a list of choices and allow the user to select one. The two kinds of dialog box are functionally equivalent but have slightly different visual styles to better simulate what a GUI might present. Specifically, the --radiolist command renders ( ) to mimic radio buttons.

and present a list of choices and allow the user to select one. The two kinds of dialog box are functionally equivalent but have slightly different visual styles to better simulate what a GUI might present. Specifically, the command renders to mimic radio buttons. --checklist displays a list of items that the user can enabled or disabled individually.

The output of each dialog variant differs but is either a single value or a list of quoted values separated by white space. For instance, --checklist , which is great for choosing one or more options, emits a list of quoted values, where each value is associated with an enabled option. An example demonstrates the operation:

RESULT=`dialog --stdout --checklist "Enable the account options you want:" 10 40 3 \ 1 "Home directory" on \ 2 "Signature file" off \ 3 "Simple password" off` Show more Show more icon

The backslash ( \ ) at the end of lines 1, 2, and 3 are continuations; everything from RESULT to off is one command. If the user enabled Home directory and Simple password, $RESULT would be "1" "3" . The arguments to --checklist are the height and width, the number of list elements at any time (you can scroll to see additional items if some are occluded), and the checklist options, where each option is a value, a description, and whether the option is initially enabled or disabled.

You can type dialog --help at any time to see the list of general and dialog -specific options. There are tons of uses for dialog .

Got pixels? Use Zenity.

Zenity is to the UNIX desktop what dialog is to simple terminal windows. You can use Zenity to open GTK+ dialog boxes from any shell script. In fact, Zenity shares many of the same features as dialog ; the only difference is that Zenity works in an X Window System environment. Zenity comes bundled with GNOME. If you don’t run GNOME, you can install Zenity separately (however, expect a large number of GTK+ libraries to be installed, too). You can also download the source of Zenity from the GNOME project pages (see Related topics for a link).

Here’s a quick example. The command:

zenity --question --text "Do you want to continue?" Show more Show more icon

produces something like Figure 3. (The machine used for demonstration is running Ubuntu 10.) If you click OK, the command returns 0 . Otherwise, it returns 1 .

Figure 3. A simple question

Like dialog , Zenity has a good number of options — perhaps even more than dialog — but the options are well named and thus self-explanatory. You’ll likely find Zenity more advantageous than dialog , especially as most computer users have an X desktop of some sort.

Zenity offers many of the same controls as dialog . Here is a snippet to collect a name:

ENTRY=`zenity --entry --text "Please enter your name" --entry-text "Your name" --title "Enter your name" if [ $? == 0 ]; then zenity --info --text "Hello $ENTRY\!" fi Show more Show more icon

Again, if the exit code of zenity is 0 , then ENTRY has the person’s name. Here is the calendar example from above rewritten to use Zenity:

DATE=`zenity --calendar --day "9" --month "1" --year "2010" --format "%Y-%m-%d" if [ $? == 0 ]; then echo $DATE fi Show more Show more icon

Although Zenity is a little more verbose — there are separate options for day, month, and year, for example — the additional switches free you from remembering the precise usage sequence of arguments. Zenity’s calendar also allows you to specify the format for output, using standard strftime() codes. The result of this command would be something like 2010-1-9 for 9 January 2010.

Zenity also provides a progress meter to show the state of an operation. It reads data from standard input line by line. If a line is prefixed with the octothorpe, or pound sign ( # ), the text is updated with the text on that line. If a line contains only a number, the percentage is updated with that number. Listing 1 shows is an example from the Zenity documentation.

Listing 1. The Zenity progress meter

#!/bin/sh ( echo "10" ; sleep 1 echo "# Updating mail logs" ; sleep 1 echo "20" ; sleep 1 echo "# Resetting cron jobs" ; sleep 1 echo "50" ; sleep 1 echo "This line will just be ignored" ; sleep 1 echo "75" ; sleep 1 echo "# Rebooting system" ; sleep 1 echo "100" ; sleep 1 ) | zenity --progress \ --title="Update System Logs" \ --text="Scanning mail logs..." \ --percentage=0 if [ "$?" = -1 ] ; then zenity --error \ --text="Update canceled." fi Show more Show more icon

The sub-shell (wrapped in parentheses) performs a series of tasks — albeit sleep delays in this contrived example — and emits output to a Zenity progress meter via a pipe. Before each step, the sub-shell emits a number to advance the progress meter, which starts at 0 per --percentage 0 , and then emits a string prefaced with # to change the status message. Thus, the progress meter steps along to mark the work of the script. If Zenity exits with code -1 , the Cancel button was clicked.

Again, to use dialog or Zenity, replace code where you previously referenced a command-line argument with a dialog box. With a little creativity, you can turn your shell scripts into first-class desktop citizens.

At some point, you may find that your requirements exceed the capabilities of both shell scripting and the dialog and Zenity tools. In those instances, you may turn to C/C++ and build native applications for the desktop, but you can also turn to advanced scripting languages and language bindings for any number of robust GUI frameworks.

One combination is the Ruby scripting language and the Ruby bindings for the wxWidgets framework. Ruby is object oriented, expressive, concise, and runs on most operating systems. The wxWidgets framework is also available on every major platform, including Mac OS X, Windows®, Linux®, and UNIX. Because both are portable, you can write an application once in Ruby and run it everywhere. Another, simpler option is Shoes. Although not as rich as wxWidgets, Shoes is fairly quick to learn and use. The code in Listing 2 realizes a calculator in 70 lines of code.

Listing 2. A calculator in Shoes

class Calc def initialize @number = 0 @previous = nil @op = nil end def to_s @number.to_s end (0..9).each do |n| define_method "press_#{n}" do @number = @number.to_i * 10 + n end end def press_clear @number = 0 end {'add' => '+', 'sub' => '-', 'times' => '*', 'div' => '/'}.each do |meth, op| define_method "press_#{meth}" do if @op press_equals end @op = op @previous, @number = @number, nil end end def press_equals @number = @previous.send(@op, @number.to_i) @op = nil end end number_field = nil number = Calc.new Shoes.app :height => 250, :width => 200, :resizable => false do background "#EEC".."#996", :curve => 5, :margin => 2 stack :margin => 2 do stack :margin => 8 do number_field = para strong(number) end flow :width => 218, :margin => 4 do %w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn| button btn, :width => 46, :height => 46 do method = case btn when /[0-9]/; 'press_'+btn when 'Clr'; 'press_clear' when '='; 'press_equals' when '+'; 'press_add' when '-'; 'press_sub' when '*'; 'press_times' when '/'; 'press_div' end number.send(method) number_field.replace strong(number) end end end end end Show more Show more icon

An introduction to Ruby and Shoes is beyond the scope of this article, but here are some of the most important constructs:

The bulk of the Ruby class Calc uses Ruby’s metaprogramming features to define functions at run time for all the digit keys and for the math operation keys.

uses Ruby’s metaprogramming features to define functions at run time for all the digit keys and for the math operation keys. The code beginning Shoes.app... creates the GUI for the calculator, rendering the layout and the buttons for it. Shoes provides two containers to assemble layouts: the stack and the flow . A stack is a vertical stack of elements, with each element placed directly beneath the element preceding it. A flow packs elements in as tightly as it can until it reaches the limits of its bounding box, and then wraps the remaining elements. (You can think of a stack as an HTML <div> and a flow as an HTML <p> .) You create a stack or flow using a Ruby block.

creates the GUI for the calculator, rendering the layout and the buttons for it. Shoes provides two containers to assemble layouts: the and the . A stack is a vertical stack of elements, with each element placed directly beneath the element preceding it. A flow packs elements in as tightly as it can until it reaches the limits of its bounding box, and then wraps the remaining elements. (You can think of a stack as an HTML and a flow as an HTML .) You create a stack or flow using a Ruby block. The innermost flow block loops, creating all the buttons in the application and effectively binding each button to its method. (The case statement returns a method name; the line number.send(method) calls that method on the instantiated calculator.)

block loops, creating all the buttons in the application and effectively binding each button to its method. (The statement returns a method name; the line calls that method on the instantiated calculator.) The line number_field.replace strong(number) updates the calculator display with the result of the most recent calculation. Emitting number causes the class to call its own to_s (“to string”) method.

Other scripting languages have similar libraries, and there are many more choices for Ruby itself, including Ruby Cocoa to develop Cocoa applications on Mac OS X with Ruby. Pick your favorite open source scripting language, find a lightweight GUI toolkit, and start coding.

We don’t need no stinkin’ compiler!