Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
The Ultimate Linux Shell Scripting Guide

You're reading from   The Ultimate Linux Shell Scripting Guide Automate, Optimize, and Empower tasks with Linux Shell Scripting

Arrow left icon
Product type Paperback
Published in Oct 2024
Publisher Packt
ISBN-13 9781835463574
Length 696 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Donald A. Tevault Donald A. Tevault
Author Profile Icon Donald A. Tevault
Donald A. Tevault
Arrow right icon
View More author details
Toc

Table of Contents (26) Chapters Close

Preface 1. Getting Started with the Shell FREE CHAPTER 2. Interpreting Commands 3. Understanding Variables and Pipelines 4. Understanding Input/Output Redirection 5. Customizing the Environment 6. Text-Stream Filters – Part 1 7. Text Stream Filters – Part 2 8. Basic Shell Script Construction 9. Filtering Text with grep, sed, and Regular Expressions 10. Understanding Functions 11. Performing Mathematical Operations 12. Automating Scripts with here Documents and expect 13. Scripting with ImageMagick 14. Using awk – Part 1 15. Using awk – Part 2 16. Creating User Interfaces with yad, dialog, and xdialog 17. Using Shell Script Options with getops 18. Shell Scripting for Security Professionals 19. Shell Script Portability 20. Shell Script Security 21. Debugging Shell Scripts 22. Introduction to Z Shell Scripting 23. Using PowerShell on Linux 24. Other Books You May Enjoy
25. Index

Creating User Interfaces with dialog and xdialog

Another slight problem with yad is that you can only use it on machines that have a desktop environment installed. But, many Linux, Unix, and Unix-like servers are set up with a full text-mode environment and don’t have to use graphical desktops. Another slight problem with yad is that even on desktop-type operating systems, it’s not always available for installation. However, if yad isn’t available and you still need a GUI solution, you might be able to use xdialog, which is more universally available. Let’s begin with a look at dialog, which can be used on text-mode machines.

The dialog Basics

dialog used to be installed by default on all Linux systems, but it no longer is. However, it’s available for installation for pretty much every Linux distro. So, you can install it with your distro’s normal package manager. On most Unix and Unix-like systems, such as OpenIndiana and FreeBSD, it still comes installed by default.

The basic dialog building blocks are known as widgets. Each widget has a set of parameters that you need to specify when you use it. For example, open the dialog man page and scroll down to the --msgbox paragraph, where you’ll see this:

--msgbox text height width

So, this widget requires you to specify three parameters. (Unlike yad, specifying the dimensions of a dialog box isn’t optional.) Let’s see how that looks in the dialog-hello.sh script:

#!/bin/bash
dialog --title "Dialog message box" \
        --msgbox "\n Howdy folks!" 6 25

As you’ve seen before in the yad section, I used a backslash to break the command into two lines, which makes things a bit more readable. On the --msgbox line, we see the message text, the height as defined by the number of rows in the box, and the width as defined by the number of characters that fit on a line.

Running the script looks like this:

B21693_16_11

Figure 16.11: Running the dialog-hello.sh script

To close the window, just hit the Enter key, which activates the OK button by default. (On a desktop machine, you also have the option of clicking on the OK button with your mouse.) Once the window is closed, run the clear command to remove the blue background from your terminal.

By default, dialog windows always appear in the center of your terminal, and the blue background won’t clear until you clear it yourself. Fortunately, you can change both behaviors. Here’s a slightly modified version of our script, which places the window at the top left corner of the terminal:

#!/bin/bash
dialog --title "Dialog message box" \
        --begin 2 2 \
        --msgbox "\nHowdy folks!" 6 25
clear

Here’s how that looks:

B21693_16_12

Figure 16.12: Placing the dialog box in the upper-left corner

The --begin option takes two parameters. The first one denotes the vertical position of the box, and the second one denotes the horizontal position. For example, by changing the line to --begin 15 2, the box would show up in the lower left corner. To place it into the upper right corner, you could change it to --begin 2 50. The clear command at the end will clear away the blue background when you hit the OK button.

Of course, this script isn’t very useful, but that’s okay. I’ll show you some more useful concepts in just a bit. First though, allow me to say a few words about xdialog.

The xdialog Basics

If you need a GUI-type of user interface and yad isn’t available for your desktop system, xdialog could be a good alternative. (I say could be, because neither yad nor xdialog is available for OpenIndiana.) It should be in the normal repository of your Linux or Unix-like distro, so just use your normal package manager to install it. The reason I can talk about both dialog and xdialog in the same section is because for the most part, code that’s written for dialog can also run with xdialog.

There’s one thing to look out for that could trip you up. For some strange reason, the package name is xdialog, in all lower-case letters. But, after it’s installed, you’ll need to invoke the program by typing Xdialog, with an upper-case X. (After I installed it, it took me a while to figure out why I couldn’t get it to work.)

For the most part, changing a dialog script to run as a GUI-type program is a simple matter of changing all instances of dialog to Xdialog, as you see here:

#!/bin/bash
Xdialog --title "Dialog message box" \
        --begin 2 50 \
        --msgbox "\nHowdy folks!" 6 25
clear

Running the script on a desktop system gives you this:

B21693_16_13

Figure 16.13: Running the script with xdialog

The first thing to note is that xdialog ignores the --begin option, and just places the box in the center of the terminal. There’s also the fact that xdialog often requires you to create boxes with larger dimensions so that you can see everything. So, let’s change that in the final xdialog-hello.sh script, like this:

#!/bin/bash
Xdialog --title "Dialog message box" \
        --begin 2 2 \
        --msgbox "\nHowdy folks!" 15 50
clear

As you see here, this makes things look much better:

B21693_16_14

Figure 16.14: The improved xdialog script

With xdialog, the clear command at the end of the script is no longer necessary, but it doesn’t hurt anything to leave it. In fact, we’ll need it for the next demo.

Automatically Choosing Either dialog or xdialog

Now, here’s something that’s really cool. With only a few extra lines of code, you can make your script automatically detect whether it’s running on a desktop or text-mode machine, and whether xdialog is installed. Here’s the xdialog-hello2.sh script to show how it works:

#!/bin/bash
command -v Xdialog
if [[ $? == 0 ]] && [[ -n $DISPLAY ]]; then
        diag=Xdialog
else
        diag=dialog
fi
$diag --title "Dialog message box" \
        --begin 2 2 \
        --msgbox "\nHowdy folks!" 15 50
clear

There are several ways to detect if a program is installed. In Chapter 12, Automating Scripts with here Documents and expect, I showed you this method in the system_info.sh script:

if [[ -f /usr/local/bin/pandoc ]] || [[ -f /usr/bin/pandoc ]]; then
        pandoc -o sysinfo.pdf sysinfo.html
        rm sysinfo.html
fi

The pandoc executable that we needed is in the /usr/local/bin/ directory on FreeBSD, and in the /usr/bin/ directory on everything else. So, I set up this if..then construct to detect if the executable is in either place. That works, but I’d now like to show you an easier way.

In the xdialog-hello2.sh script, the command -v Xdialog command detects if the Xdialog executable file is present, regardless of which directory it’s in. If it is, the command will return exit code 0. If it’s not, the command will return exit code 1. To see how this works, go ahead and run this on the command line. Here’s how it looks if the Xdialog executable isn’t detected:

donnie@ubuntu2204:~$ command -v Xdialog
donnie@ubuntu2204:~$ echo $?
1
donnie@ubuntu2204:~$

And, here’s how it looks if it is detected:

donnie@fedora:~$ command -v Xdialog
donnie@fedora:~$ echo $?
0
donnie@fedora:~$

In the next line, you see an if..then construct that checks for two things. First, it checks the exit code from the command command, and then it checks to see if a graphical desktop environment is installed. If the value of the DISPLAY environmental variable is of a non-zero length, then a desktop environment is installed. You can see how this works by running the echo $DISPLAY command yourself. Here’s how it looks on a desktop machine:

donnie@fedora:~$ echo $DISPLAY
:0
donnie@fedora:~$

On a text-mode machine, you’ll get no output at all, as you see here on this Ubuntu Server virtual machine:

donnie@ubuntu2204:~$ echo $DISPLAY
donnie@ubuntu2204:~$

For our purposes, we can say that the value of DISPLAY is zero characters long on this text-mode machine.

Now, let’s take another look at our if..then statement:

if [[ $? == 0 ]] && [[ -n $DISPLAY ]]; then
        diag=Xdialog
else
        diag=dialog
fi

This means that if the exit code from the command -v Xdialog line is 0, and the value of the DISPLAY environmental variable is of a non-zero length, then the value of the diag variable becomes Xdialog. If either the Xdialog executable is missing or the value of DISPLAY is not a non-zero length, then the value of diag becomes dialog. What makes this even more cool is that this works the same on all Linux, Unix, or Unix-like systems. I’ve tested this script on FreeBSD, GhostBSD, DragonflyBSD, OpenIndiana, and on both desktop and text-mode implementations of Linux. On all of them, the script correctly detects everything that it’s supposed to detect, and correctly chooses whether to run either dialog or Xdialog.

Here’s something that caused me a bit of consternation. I accidentally found out that the order in which you test for things sometimes matters. In the if..then construct of this script, I originally checked for the value of DISPLAY first, and then checked for the exit code of the command command. The script wouldn’t run correctly like that, because the test for the DISPLAY value was setting the exit code to 0. When I reversed the order of the tests, everything began to work correctly.

Go ahead and try running this script on a variety of systems, just to see what happens.

Next, let’s build on what we’ve already done by adding another widget.

Adding Widgets

You can add more functionality by adding more widgets. Take for example the dialog-hello2.sh script, which you can download from the Github repository. I can’t show the entire file at once due to formatting constraints, so I’ll show it to you a section at a time. Here’s the top section:

#!/bin/bash
command -v Xdialog
if [[ $? == 0 ]] && [[ -n $DISPLAY ]]; then
        diag=Xdialog
else
        diag=dialog
fi

We’ve already seen this in the previous script, and I’ve already explained it. So, let’s move on. Here’s the next section:

$diag --title "Dialog message box" \
        --begin 2 2 \
        --msgbox "\n Hello world!" 20 50
$diag --begin 4 4 --yesno "Do you believe in magic?" 0 0

The first $diag line creates the initial message box in the top left corner, as specified by the --begin 2 2 option. The second $diag line creates a box with two buttons that are labeled as Yes and No. The --begin 4 4 option positions the yesno box just slightly lower and slightly more to the right of where the initial message box was. After --yesno, we see the text that we want the box to display and the height and width parameters. (Setting the height and width to 0 and 0 causes the box to size itself automatically.)

Next, we have a case..esac construct that assigns commands to the two buttons. Remember that when clicked, the Yes button returns exit code 0, and the No button returns exit code 1. Pressing the Esc key returns exit code 255. We can use these exit codes to trigger a desired command, as you see here:

case $? in
        0)
                clear
                echo "Cool!  I'm glad that you do." ;;
        1)
                clear
                echo "I'm sorry that you live such a dull life." ;;
        255)
                clear
                echo "You pressed the ESC key." ;;
esac

Okay, all this script does is display a message when you press a button. But, it does serve to demonstrate the concept, so it’s all good.

You’ve already seen the initial message box, so I won’t show that again. Instead, I’ll show the yesno box that comes up next:

B21693_16_15

Figure 16.15: The yesno box

When I click the Yes button, I’ll see the appropriate message, as you see here:

B21693_16_16

Figure 16.16: After clicking on the Yes button

And of course, this script runs equally well with xdialog on a machine that supports it.

Next, let’s create something that will actually do some useful work for us.

Creating an SSH Login Interface

Let’s say that you have a fleet of Linux, Unix, or Unix-like servers that you need to administer remotely via SSH. Trying to keep track of the server IP addresses is a confusing mess, so you’ve decided to simplify things. You’ve decided to create the xdialog-menu-test.sh to help you out. It’s also too long to show here in its entirety, so I’ll break it up into sections. Here’s the top part:

#!/bin/bash
command -v Xdialog
if [[ $? == 0 ]] && [[ -n $DISPLAY ]]; then
        diag=Xdialog
else
        diag=dialog
fi

This is the same as it was in the previous script, so you already know about it. Here’s the next section:

cmd=($diag --keep-tite --menu "Select options:" 22 76 16)
options=(1 "Remote SSH to Debian miner"
         2 "Remote SSH to Fedora miner"
         3 "Remote SSH to Fedora Workstation"
         4 "Remote SFTP to Fedora Workstation")
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)

The cmd= line creates a command within a pair of parentheses, and then assigns that command to the cmd variable array. This command, which will be invoked in the final line of the script, will build either a dialog or xdialog menu window. According to the dialog man page, the --keep-tite option is desirable when running the script in a graphical terminal emulator on a desktop machine, because it prevents the script from switching back and forth between the graphical terminal and the underlying shell terminal. After the --menu option, we see the text that is to be displayed, the height and width of the menu box, followed by the maximum number of menu entries that will be displayed at a time. (In this example, the user would need to scroll down to see past the first 16 menu entries.) The menu entries would be designated with a tag and an item string. For example, the first menu entry we see has a 1 as the tag, and Remote SSH to Debian miner as the item.

Next, we have the options stanza, which inserts all of the menu entries into an array. The choices= line uses the 2>&1> redirection operator to dump the contents of the array onto the terminal screen (/dev/tty). The @ in ${options[@]} is a form of variable expansion, because it allows an action to be carried out according to which menu item that a user chooses. The @ represents the index number of the array item that corresponds with a menu choice.

In Chapter 8, Basic Shell Script Construction, I showed you how to use either the * or the @ in place of a specific index number to show all elements in an array. In the cmd= line, each component of the $diag --keep-tite --menu "Select options:" 22 76 16 command that’s within the parentheses is a separate element of the cmd array. For this reason, we need to use either the * or the @ as the index for the cmd array in the choices= line at the bottom, so that the entire command will get invoked. The same thing is true for the list of options that get assigned to the options array in the options= line, as you’ll see in a moment.

Now that we have a menu, we need to make it do something. That comes in the next section, which you see here:

for choice in $choices
do
    case $choice in
        1)
            ssh donnie@192.168.0.3
            ;;
        2)
            ssh donnie@192.168.0.12
            ;;
        3)
            ssh donnie@192.168.0.16
            ;;
        4)
            sftp donnie@192.168.0.16
            ;;
    esac
done

This is nothing that you haven’t seen before. It’s just a normal for loop that operates a case..esac construct. What is a bit different is that the value of the choices variable is the index number of the array item that corresponds with the chosen menu item. Each of the listed options in the options array consists of a number, followed by a complete phrase, such as 1 "Remote SSH to Debian miner". In this example, the number 1 represents element number 0 of the options array, and the following words in the phrase represent elements 1 through 5. In order for the options to properly display in the menu, you’ll have to invoke the array with either * or @ in place of the index number. Here’s the complete line where that happens:

choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)

Since only one index value is ever assigned to choices, the for loop exits after its first run.

Now that everything is built, let’s see if it works. Here’s how it looks using xdialog on GhostBSD with the Mate desktop:

B21693_16_17

Figure 16.17: The xdialog-menu-test.sh script on GhostBSD

And, here’s the exact same script on Fedora with dialog:

B21693_16_18

Figure 16.18: The xdialog-menu-test.sh script on Fedora with dialog

When you choose a menu item, the script will open a remote login prompt in the terminal, and then exit. That’s okay, because if you want to connect to multiple remote servers at once, you’ll need to open other terminals anyway.

Of course, you can fancy this script up to make it even more functional. For example, you can configure your SSH client to use different profiles or different encryption keys for different server sessions, and modify the commands in the menu accordingly. Or, you can use this as a template for something else altogether. As I keep saying, let your imagination run wild!

All right, I think that this about does it for yad, dialog, and xdialog. Let’s wrap up and move on.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image