Chapter 15

Automating RPM with Scripts

Copyright (c) 2005 by Eric Foster-Johnson. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/).

In This Chapter

*	Deciding when to program and when to script

*	Examining RPM files with scripts

*	Querying the RPM database with scripts

The rpm command provides a very high-level view of package management. Most of the operations you need to perform require only a single invocation. Some of the command-line options to the rpm command tend to get very complex, however, especially for detailed queries. That’s where scripting can help.

This chapter covers scripting, specifically shell scripting, with the rpm command, especially for newcomers to Linux scripting

Scripting

Scripting allows you to quickly write new commands in a language, called a scripting language, that can help automate your work. Used heavily by system administrators and lightly by software developers, scripts can help remove some of the tedium from your day-to-day tasks. Scripts can also hold the complex query formats used with the rpm command so you don’t have to remember them.

Scripts start out as text files. These text files hold commands in the scripting language. Most of these script file commands run commands installed on your system, such as rpm. To run a script, invoke a command, called an interpreter, that reads in the script file and executes the commands inside the script.

Programming is usually considered different from scripting, even though there are many similarities. Programs start out as text files. These text files hold commands in the programming language and sometimes, not often, calls to commands installed on your system. Programs generally involve more work than scripts and are generally larger, containing more commands.

Furthermore, most programs need to be compiled. A separate command parses the program text files and generates some form of machine code. Multiple pieces of a program may be linked together to form a command you can call from your shell prompt.

Some programming languages, such as Java or C#, are compiled to a generic bytecode format. A compiled Java program, for example, is the same no matter what the architecture. To run such a program, you need a runtime engine such as the java command provides. (Runtime engine is a fancy term for interpreter.)

Such differences between scripting and programming sometimes get in the way of performing real work. For example, I once worked with a group of people who were convinced that they were not programmers. They felt that programming was an art that was far beyond them. Yet, they wrote hundreds of kilobytes of scripts to create a sophisticated graphical interface for a Computer-Aided Design system. In my mind, they were programming (and doing quite well at it). In their minds, though, there was a clear distinction between scripting@mdwhat they could do@mdand programming, which was beyond them, they thought.

Don’t get caught up in this. Use the right tool for the job.

Distinguishing Scripting Languages from Programming Languages

Experts differ regarding what defines a scripting language and what defines a programming language. It’s clear that languages such as Python blur the old distinction between programming and scripting.

Originally, scripting was writing small files of commands that invoked other system commands. For example, you could write a script that wraps the Linux file command. Scripts were executed by scripting-language interpreters that parsed each command one at a time and then executed the command.

Modern scripting languages, such as Tcl, are parsed at runtime and compiled into an internal bytecode format. Once compiled, there is no real difference from a language associated with programming such as Java.

With a scripting language

*	You generally don’t have to compile the script in advance. The scripting language interpreter may compile the program, often to an internal byte code, but you don’t have to invoke a compiler as a separate step.

*	The facilities of the language generally provide a higher level and more abstract level of interaction with the system than with programming languages. For example, writing socket-based networking code in Tcl requires a lot less code than writing the same code in a programming language such as C. Tcl provides a more abstract view of networking; therefore, your code is a lot simpler.

*	The commands in the scripting language are mostly the commands available to you on the command line. Scripting languages introduce their own commands, too.

*	The language is generally identified as a scripting language. This is more consensus than anything else. Forth is considered an interpreted programming language, while Perl is considered a scripting language.

Table 15-1 lists some of the more common scripting and programming languages. Note that these are the generally-accepted categories for these languages, not hard and fast rules. This should not stop you, for example, from writing programs in Perl or Python. The distinctions between programming and scripting have blurred in recent years.

Table 15-1 Common Scripting Languages and Common Programming Languages

Scripting Languages	Programming Languages
Bash (Bourne Again shell) Csh (C shell) JavaScript Ksh (Korn shell) Lua MS-DOS batch files Perl Python Ruby Sh (Bourne shell) Tcl	Assembler BASIC C C++ C# FORTRAN Forth Java LISP Modula-2, Modula-3 Oberon Pascal

Deciding When to Program and When to Script

Just as the distinction between programming and scripting languages has blurred in the last few years, so have the guidelines for when you should program and when you should script. The simplest rule remains, though: Use whatever techniques make you productive. In the end, no one really cares if you call it a program or a script.

Even so, these guidelines may help:

*	If you have to perform a lot of operations on a lot of RPMs, a program will likely perform much faster than a script that calls the rpm command over and over.

*	If the task is relatively simple, scripting generally works best.

*	If you are more experienced with a particular language, use it.

*	If you need to perform complex operations, perhaps involving transactions, a program is probably the right way to go.

*	In many cases, programming languages work better for creating graphical user interfaces, although Python and Perl offer graphical user interface toolkits, such as Perl/Tk or PyQt.

There isn’t one right way to do it. Pick what works best for you.

Cross Reference

This chapter covers shell scripting. Chapter 16 covers C programming. Chapter 17 covers Python scripting and programming, and Chapter 18 covers Perl scripting.

Shell Scripting Basics

For newcomers to scripting, don’t worry. A script, in this case a shell script, is merely a text file with commands mostly the same as the commands you can type at the keyboard. I’ll point out the differences.

The following sections quickly introduce scripting for those new to this venture.

Writing a script

For your first venture, enter the following script into a text file:

rpm -qa | grep rpm

This script has a two-part command. The rpm –qa part queries all RPM packages, as covered in Chapter 4. The grep rpm part finds only packages with rpm in their names. This is a very simple script, but it can serve to show how to work with scripts.

Save this file under the name listrpmpkgs, since this script lists RPM packages.

Note

If you’re new to Linux, you’ll notice there’s no program named Notepad.exe. There are, though, a plethora of Linux text editors to choose from. See Appendix F for a listing of Linux text-editing tools.

Running a script

Once you’ve entered a script, you can run it with the sh command, as shown following, passing the name of your script to the sh command:

$ sh listrpmpkgs

librpm404-devel-4.0.4-8x.27

librpm404-4.0.4-8x.27

rpm404-python-4.0.4-8x.27

rpm-4.1-1.06

rpm-devel-4.1-1.06

gnorpm-0.9-1

rpm-python-4.1-1.06

redhat-rpm-config-8.0-1

rpm-build-4.1-1.06

rpmrebuild-1.0-0

Type the command you have placed in your script at the command line. There should be no difference in the output. For example:

$ rpm -qa | grep rpm

librpm404-devel-4.0.4-8x.27

librpm404-4.0.4-8x.27

rpm404-python-4.0.4-8x.27

rpm-4.1-1.06

rpm-devel-4.1-1.06

gnorpm-0.9-1

rpm-python-4.1-1.06

redhat-rpm-config-8.0-1

rpm-build-4.1-1.06

rpmrebuild-1.0-0

Problems running scripts

The previous script example required the sh program, a Linux shell, to run the script. You also had to have the script file, such as listrpmpkgs, available. So, if you have stored the file in /home2/bin, to run the script, use the following command:

$ sh /home2/bin/listrpmpkgs

That’s not very convenient. Furthermore, you always have to remember where you stored the script file listrpmpkgs. To make this command work better, you can turn your script into a command.

Turning a script into a command

To turn a script into a command, do three simple things:

1.	Add a special magic comment to the start of the file so Linux recognizes your text file as a command script.

2.	Change the permissions on the file so that it is marked as executable.

3.	Copy the file to a directory located in your command path.

Shell scripts use a # to indicate a comment, text intended for human readers that can help explain the purpose of the script. By convention, Linux shells use a #! comment in the first line of a script file as a special marker that indicates the file is a shell script. The text that comes after the #! holds the name of the command that should be used to run the script. In almost all cases, that command should be /bin/sh for a shell script.

So edit the listrpmpkgs script again, and add the magic comment so that the file reads as follows:

#!/bin/sh

rpm -qa | grep rpm

Make sure the #! comment starts at the beginning of the first line.

Next, change the permissions on the script to mark it as an executable program. Use the chmod command to do this. The chmod command changes the file permissions. To see the permissions, run the ls –l command before changing the permissions:

$ ls -l listrpmpkgs

-rw-rw-r-- 1 ericfj ericfj 31 Nov 7 20:02 listrpmpkgs

The first set of characters, the -rw-rw-r--, indicate the permissions in three batches: permissions for the file owner, the owner’s group of users, and world (everyone else). The rw means read and write, and the r alone means read only for everyone not the owner and not in the owner’s group.

To add the permission to execute the file for the file owner only, use the following command:

$ chmod u+x listrpmpkgs

In this command, the u stands for the user who owns the file (for historical reasons, an o stands for others, not owner). The +x means add the x permission, short for execute permission.

After running this command, you can see the revised permissions.

$ ls -l listrpmpkgs

-rwxrw-r-- 1 ericfj ericfj 31 Nov 7 20:02 listrpmpkgs

Cross Reference

Use the man chmod command to see more information on this command.

You now have a command you can run locally. For example:

$ ./listrpmpkgs

librpm404-devel-4.0.4-8x.27

librpm404-4.0.4-8x.27

rpm404-python-4.0.4-8x.27

rpm-4.1-1.06

rpm-devel-4.1-1.06

gnorpm-0.9-1

rpm-python-4.1-1.06

redhat-rpm-config-8.0-1

rpm-build-4.1-1.06

rpmrebuild-1.0-0

The next step is to copy the file to a directory in your system command path. To see which directories are in your path, run the following command:

$ echo $PATH

/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/ericfj/bin:/usr/java/j2sdk1.4.0_01/bin

Pick one of these directories. The /usr/local/bin directory is a common place to share locally created commands. If this is a personal command for your own use only, a directory under your home directory will be better. In this example, the /home/ericfj/bin is one such directory.

Copy the script file to a directory in your command path, and you are ready to go.

Note

If you use the C shell, csh, or the T C shell, tcsh, you need to run the rehash command to tell the shell to look again at the set of commands available in your command path.

Enter the following command:

$ listrpmpkgs

librpm404-devel-4.0.4-8x.27

librpm404-4.0.4-8x.27

rpm404-python-4.0.4-8x.27

rpm-4.1-1.06

rpm-devel-4.1-1.06

gnorpm-0.9-1

rpm-python-4.1-1.06

redhat-rpm-config-8.0-1

rpm-build-4.1-1.06

rpmrebuild-1.0-0

You have now extended the Linux command set with your own command.

Note

Windows users may be used to the convention that program file names end in .exe and scripts end in .bat or .cmd. When you run these programs or scripts, you don’t include the extension, exe, .bat, or .cmd. With Linux and UNIX, though, the full file name is important, so if you name your script rpminfo.bat, you must type rpminfo.bat each time you run the script. That’s why most Linux programs and scripts have no filename extension.

If you want to share your script with others, you should give them the right to execute it as well. You can do that with the following command:

$ chmod a+x listrpmpkgs

In this case, the a stands for all users.

Passing command-line options to your script

The listrpmpkgs script used so far isn’t very useful. It performs one command and that’s it. We cannot customize it without writing a new script.

One way to make a script more flexible is to allow it to use command-line options. Just like the rpm command accepts a zillion options, you can make your scripts accept options.

Shells define special variables for the command-line options passed to the shell. Table 15-2 lists these options.

Table 15-2: Shell variables for command-line options

Variable	Holds
$0	The name of the script itself, from the command line
$1	The first option
$2	The second option
$3	The third option
$4	The fourth option
$5	The fifth option
$6	The sixth option
$7	The seventh option
$8	The eighth option
$9	The ninth option
$*	All command-line options
$#	Holds the number of command-line options

Note

Use $#argv in place of $# if you use the C shell to run your scripts.

You can use these variables to allow the user to pass the text to search for, instead of always searching for rpm. With this addition, your new script, renamed rpmgrep, follows in Listing 15-1:

Listing 15-1: rpmgrep

#!/bin/sh

rpm -qa | grep $*

This script now expects a command-line option that holds the text to search for. Mark this script as an executable; then you can run it as follows:

$ ./rpmgrep python

python-devel-2.2.1-17

gnome-python2-gtkhtml2-1.99.11-8

gnome-python2-canvas-1.99.11-8

gnome-python2-1.99.11-8

rpm404-python-4.0.4-8x.27

orbit-python-1.99.0-4

gnome-python2-bonobo-1.99.11-8

gnome-python2-gconf-1.99.11-8

libxslt-python-1.0.19-1

libxml2-python-2.4.23-1

python-optik-1.3-2

python-2.2.1-17

rpm-python-4.1-1.06

mod_python-3.0.0-10

python-tools-2.2.1-17

If you want to make this command available, copy it to a directory in your command path as described in the preceding section.

Examining RPM Files

When you work with a lot of RPM files, you’ll find that you run the same commands over and over again for each new package you get. For example, you may want to see what capabilities a package requires. You can type in the rpm command each time, or write a short shell script with the necessary command-line options. Listing 15-2 shows this script.

Listing 15-2: rpmdepend

#!/bin/sh

rpm -qp --requires $*

This script expects the name of an RPM package file. Run the command as follows:

$ rpmdepend vim-common-6.1-14.i386.rpm

rpmlib(PayloadFilesHavePrefix) <= 4.0-1

rpmlib(CompressedFileNames) <= 3.0.4-1

/bin/sh

/usr/bin/awk

libc.so.6

libc.so.6(GLIBC_2.0)

libc.so.6(GLIBC_2.1)

Another common task I perform involves listing all the files in an RPM along with the descriptive information on the package. This can really help, since so many Linux packages have nondescriptive names such as dia and anaconda.

Listing 15-3 shows the rpminfo script.

Listing 15-3: rpminfo

#!/bin/sh

rpm -qilp $* | less

This script lists a potentially long set of lines, so the script pipes the output to the less command. For example:

$ ./rpminfo perl-XML-Dumper-0.4-22.noarch.rpm

Name : perl-XML-Dumper Relocations: /usr

Version : 0.4 Vendor: Red Hat, Inc.

Release : 22 Build Date: Tue 06 Aug 2002 01:53:30 PM CDT

Install date: (not installed) Build Host: vegeta.devel.redhat.com

Group : System Environment/Libraries Source RPM: perl-XML-Dumper-0.4-22.src.rpm

Size : 10015 License: GPL

Signature : DSA/SHA1, Tue 06 Aug 2002 02:11:39 PM CDT, Key ID fd372689897da07a

Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>

URL : http://www.cpan.org

Summary : Perl module for dumping Perl objects from/to XML

Description :

XML::Dumper dumps Perl data to XML format. XML::Dumper can also read

XML data that was previously dumped by the module and convert it back

to Perl. Perl objects are blessed back to their original packaging;

if the modules are installed on the system where the perl objects are

reconstituted from xml, they will behave as expected. Intuitively, if

the perl objects are converted and reconstituted in the same

environment, all should be well.

/usr/lib/perl5/vendor_perl/5.8.0/XML/Dumper.pm

/usr/share/man/man3/XML::Dumper.3pm.gz

I use this script so that I know what files a package wants to install.

Querying the RPM Database

In addition to querying RPM files, you can script the commands you use to query the RPM database. This is most useful for the long commands with query formats, especially if you have a hard time remembering all the formats.

Querying for all packages installed at the same time

If you want to list all the packages that were installed with the same transaction ID as a particular package, for example, you can use a script like rpmtran, in Listing 15-4.

Listing 15-4: rpmtran

#!/bin/sh

tid=`rpm -q --qf "%{INSTALLTID}\n" $*`

rpm -q --tid $tid

This script uses the query format to get the transaction ID, or tid, for a particular package. It then passes this transaction ID to the rpm command to query for all packages installed with the same transaction ID.

For example:

$ ./rpmtran tcl

itcl-3.2-74

tclx-8.3-74

tcl-8.3.3-74

tix-8.2.0b1-74

tkinter-2.2.1-17

Reading HTML documentation for a package

You can combine the rpm command with other commands as well. For example, the rpm –qd command lists the documentation files with a package. If this documentation is in HTML format, you can display this documentation in a Web browser such as Mozilla. Furthermore, by convention, the starting page for HTML documentation should be a file named index.html. Listing 15-5 combines all these factors:

Listing 15-5: rpmmoz

#!/bin/sh

html_file=`rpm -qd $* | grep index.html | head -n 1 `

echo "Launching Web browser with $html_file"

htmlview $html_file &

This script searches for the documentation for a given package name, finds the first file named index.html, and launches the Web browser in the background to display this file, using the htmlview command which will likely run mozilla or your configured Web browser. When you run this command, you should see output like the following; then the Web browser should appear:

$ ./rpmmoz rpm-devel

Launching Web browser with /usr/share/doc/rpm-devel-4.1/apidocs/html/index.html

Note

This script does not check for errors. If there are no files named index.html, the script launches the Web browser anyway. You could fix this by changing the script to validate the html_file variable prior to launching the Web browser.

Where to Go From Here

This chapter just introduces the basics for shell scripting. There are many more things you can do. The online manual pages for the bash or tcsh commands provide a wealth of reference information on these shells.

A number of Web sites provide tutorials on bash, including http://pegasus.rutgers.edu/~elflord/unix/bash-tute.html and www.linuxorbit.com/modules.php?op=modload&name=Sections&file=index&req=viewarticle&artid=459. In addition, the Linux Documentation Project at www.tldp.org/guides.html provides a bash scripting guide, along with a number of bash- and shell-related how-to documents at www.tldp.org/HOWTO/HOWTO-INDEX/howtos.html.

Teach Yourself Linux, by Steve Oualline and Eric Foster-Johnson (John Wiley & Sons, 2000), introduces a number of Linux topics, including text editors and scripting, for those new to Linux. And Graphical Applications with Tcl and Tk (Hungry Minds, Inc., 1997) by Eric Foster-Johnson, covers another scripting language, Tcl/Tk.

Use your imagination. Any command that you run often or that is hard to type can be scripted. Furthermore, you can write complex scripts that automate some of the more tedious tasks you need to perform.

Summary

Scripting is the art of writing a set of commands into text files to speed up your work. Programming is the art of writing a set of commands into text files, compiling the text files, and getting paid more. Choosing when to program and when to script isn’t always a clear-cut decision, but generally programs are move involved and complex, while scripts are shorter tools that automate your work. This chapter provides just a glimpse of all you can do with scripts and the RPM system.

Scripts work very well for capturing obscure syntax options for the rpm command, especially the query formats. You can figure out a command once and then save the command as a script to save time in the future.

Scripts aren’t the best choice for all tasks, though. In many cases, you need to write a program to accomplish your goals. The next chapter delves into the RPM C programming API, rpmlib.

Chapter 15

Chris Ricker

2003-01-25T19:14:00

Eric Foster-Johnson