Practical Python in Linux
Practical Python in Linux
HELLO PYTHON
Well introduce you to Python, an easy-to-learn scripting language, and also help you get started creating your own practical Python scripts. BY LEEN FRISCH
After the initial shell designation, the programs single statement prints the specified character string to standard output. In Python, simple things are often simple, as is the case with the print statement. Python also has an interactive mode, which you can reach by issuing the python command:
$ python >> print 'Hello, world!' Hello, world!
ver the course of my career as a system administrator, Ive written scripts in a variety of languages and environments (beginning with DCL under VMS). Once I began taking care of UNIX-based systems, I gravitated first toward the C shell and then to Perl, which I used for many years. More recently, however, I have begun using Python. Although the choice of a scripting language is inevitably a personal one, and effective scripts can be written in any language, Python nonetheless offers many advantages to system administrators. For me, the most important advantages are: Python is fast to learn. Administrators with some scripting experience will be writing Python programs in minutes or hours rather than days or weeks. Python is more like traditional programming languages than other scripting options. It encourages you to think
and design tools using an object framework. Pythons easy-to-use interactive mode makes exploring new features and debugging scripts very easy. Writing tools in Python often provide cross-platform functionality for free. The Python community is generally open and friendly and considers readable code one of its core values. In this article, Ill introduce you to Python by showing you a variety of actual code snippets and short scripts, copiously annotated and commented upon, allowing you to learn about the language in context.
First Steps
Here is the canonical first Python program, which is exceedingly simple:
#!/usr/bin/python print 'Hello, world!'
i[2:4]=[3,4,5]
22
W W W. L I N U X - M A G A Z I N E . C O M
COVER STORY
With just a little experimentation, you can learn quite a lot about Python variables and data types, as shown in Listing 1. Listing 2 is a simple script that illustrates several other important Python features. It queries a POP server for the status of a specified users mailbox. The script in Listing 2 has several notable points. First, the import statement indicates Python modules that will be used in the script (modules provide additional capabilities over the built-in Python constructs). Here I am using the module related to the POP3 protocol and the system module required for script argument access. The latter are stored in a list (array) named sys.argv[]. Individual elements are selected with the familiar square bracket notation. Numbering begins at zero; sys.argv[0] holds the first element from the command line, namely, the script name. In this script, the variable who is used to store the first argument, which is the user account whose mailbox should be queried. The script also defines two variables holding literal strings. The final section of the script is where the actual work gets done. First, it opens a connection to the specified mail server system via the POP3_SSL() function in the poplib module (note the naming convention: poplib.POP3_SSL()). The connections handle (identifying data structure) is stored in the variable m. The remainder of the script takes advantage of various methods (functions) that are defined for POP3 connections in the poplib module. The user() and pass_ () methods transmit authentication data to the server, the stat() method retrieves
the number of messages present in that users mailbox (as well is the amount of space they consume), and the quit method closes the connection. The syntax for the method calls uses the standard object-oriented format, in which the method name is appended to the object to which it applies, separated by a period, followed by parenthesized arguments for example, m.user(who). The stat() method returns a two-valued tuple containing data about the mailbox: (#messages, #bytes). The script selects just the first element for printing with the [0] suffix. The print statement is a bit more complicated here; it includes a format string and a parenthesized list of items to print (another tuple), separated by a percent sign. Here, %d and %s specify an integer and a string value, respectively. Later, youll see more examples of format codes. Even this simple script shows how powerful Python can be, given that querying a network server takes only a few simple statements. Many Python modules are available, including modules for all major network servers and services. Tuples and lists are two examples of collections: variables that hold multiple values. They are also known as sequence objects because their elements are ordered. Table 1 shows some examples of literal tuples and lists. The final example illustrates slicing a list; you can also slice tuples and character strings.
execution. The script in Listing 3, which converts temperatures between different temperature scales, also illustrates script argument parsing and some string manipulation methods. Listing 3 introduces several important control structures: try/except, if/else, and for in. The try/except construct attempts the statement specified by try and executes those following except statements should the try statement fail. Note that each keyword is followed by a colon and that the statement blocks following them are indicated by indentation. In general, Python relies on indentation to identify structural blocks (rather than, say, curly braces as in C). These two features are shared by the two if/else constructs later in the script. The for loop is similar to those found in other scripting and programming languages. It has the general form for var(s) in list:, and the specified variables are assigned successive elements from the list at the beginning of each loop iteration. In Listing 3, the list was stored in the variable olist, which contained a list of tuples. Two variables were specified in the for loop, and they were assigned to the first and second elements of each tuple in succession.
W W W. L I N U X - M A G A Z I N E . C O M
23
COVER STORY
the argument list and a string indicating valid argument letters (and other infor-
mation). Here, it is passed all of the script arguments except the final one (which was previously stored in the variable temp), and the valid arguments are defined as -c, -k, -f and their uppercase counterparts. The function checks specified arguments for validity in the presence of required values, generating errors as appropriate. Once the getopt() parsing is complete, the script goes on to process the arguments as appropriate. In this case, the script performs a string comparison for equality. Python supports several comparison operators: == (equality), != (inequality), and < and > (less than and greater than, respectively, either of which may be followed by =). Complex logical conditions can be constructed with parentheses and the keywords and, or, and not.
String Operations
#getopt returns a list of (-x, val) #tuples in olist
15 olist,alist = getopt(sys.argv[1:-1],"cCfFkK") 16 for opt,a in olist: 17 18 19 20 21 22 t=eval(temp) 23 if is_c==0: # Fahrenheit to Celsius 24 25 ftemp=t ctemp=(t-32.0)*.55 # Celsius to Fahrenheit 27 28 ctemp=t ftemp=(t*1.8)+32.0 #Covert string to number and do the math if opt.lower()=='-c': is_c=1 if opt.lower()=='-k': print_k=1 #Loop: variables are assigned elements #of each tuple in olist #Covert option to lowercase and compare
26 else:
29 ktemp=ctemp+273.0 30 31 if print_k: 32 print "%.1f Fahrenheit is %.1f Celsius is %.1f K." % (ftemp,ctemp,ktemp) 34 else: 35 print "%.1f Fahrenheit is %.1f Celsius." % (ftemp,ctemp) #Print Kelvin only if requested
Listing 3 introduced the lower() method for strings. The corresponding upper() method for conversion to uppercase and a title() method for title-style capitalization also exist. Listing 4, which downloads a file from a website and processes its contents, introduces string searching and substring extraction. The first part of the file initiates an HTTP connection to a site on the Internet and retrieves a file containing currency exchange rate data, ultimately storing its contents into the variable rates. The second part of the script extracts the most recent rate for the US dollar and the foreign currency specified as the scripts first argument. For each currency, the data in rates is searched for a specified character string according to the index() method, which returns the character location of the first occurrence. A slice is used to extract the relevant line from rates, beginning at the location returned by index() and continuing through the location of the first subsequent carriage return-linefeed. The latter illustrates the use of the optional second argument to the index() method, which indicates the location within the string at which to begin searching (the default is the beginning, character 0). Here is an example of the data that would be extracted for a specific currency (in this case, yen):
intlline=Yen,0.0086864, 0.008691,0.008730,0.008731,
24
W W W. L I N U X - M A G A Z I N E . C O M
COVER STORY
0.008465,0.008513 intlline.split(",")[-1]=0.008513
The split() method divides a string into a list of elements delimited in the original string by a specified separator character. Here, I use split() with a comma separator character to divide the extracted string, and I use the [-1] suffix to retrieve the final element within the desired line (the most recent exchange rate). The script concludes by computing the exchange rate between the specified foreign currency and the US dollar (on the basis of Canadian data available on the Internet) and printing the result. The final conditional statement ensures that the printed information is in the most easily understood form. The format codes in these print statements include those for floating-point numbers (f), along with output field width values of four and six characters (respectively), with two places to the right of the decimal point for both. Other useful string methods of which you should be aware are replace(),
19 else:
which replaces of substring with new content, find(), a more powerful substring location method, and strip() and its variants lstrip() and rstrip(), which remove leading and trailing white space.
So far, I have considered a couple of types of network operations, as well as string manipulation and arithmetic commutations. In this next script, I turn to the filesystem.
Advertisement
W W W. L I N U X - M A G A Z I N E . C O M
25
COVER STORY
Python provides an easy mechanism for traversing a directory tree to potentially operate on some or all of the files found within it the walk() method within the os module.
other languages. Heres an example of a literal dictionary and two assignments involving dictionary elements:
temps={"boil":212, U "freeze":32, "scald":120, "cold":10} temps["hot"]=110 t=temps.get("tepid",66) # Return requested value or specified default if undefined
The second statement adds an element to the dictionary for the key hot (value 110). The final statement retrieves the value for the key tepid, if such an element exists; otherwise, it returns the value 66. The script in Listing 6 produces a report of error types and counts in the /var/log/messages logfile. The file defines a general function that opens and reads a logfile, splits each line into fields at spaces and other white space, and compares a specified field (index which) to the string stored in match, looking for relevant lines. When a relevant line is found, the data in the field designated in collect becomes a dictionary key; the corresponding value is the count of occur-
rences for that key within the file. The dictionary get() methods default value feature is used to initialize a count for new keys to 1. The second portion of the script calls the generic function Ive created, looking for messages from the sudo facility within /var/log/messages and recording the user name present in each entry. This code also illustrates the use of a variable for the format string within a print statement. The format codes within it include what appears to be a negative field width value; in fact, the minus sign indicates that the output should be leftjustified within the field. Here is some sample command output:
sudo Command Usage by User User Times aefrisch 5 chavez 191 root 100
26
W W W. L I N U X - M A G A Z I N E . C O M
COVER STORY
probably Tkinter. Listing 7 Pressing the Quit button is a simple script that ilinvokes the windows quit lustrates some of Tkinters method, which terminates most basic features. the event loop and deListing 7 imports the stroys the window. Figure 1: Tkinter lets you required module, again I will now examine a create a Python GUI in just using the syntax that more complex GUI utility a few steps. allows you to omit the for signaling system daemodule name prefixes mons, designed to restart from method invocations. Next, it creor reinitialize running servers on deates the root window, in which all other mand. It generates checkboxes for varigraphical entities will reside, and sets its ous daemons dynamically according to title text. Then it adds a text label and a the entries in its configuration file. Some button to the window. Each item must sample entries are as follows: call its pack() method before it actually appears. The script ends by entering the Label:Type:Name-or-Command main event loop, waiting for user input Cron:i:cron to which it will respond. Syslog:h:syslogd The button creation statement specixInetd:h:xinetd fies the window in which the button will Mail:s:/etc/init.d/ U reside, the text that will appear on it, the postfix reload color of that text, and a command that Printing:i:cups will be executed when the button is pushed. The latter is known as a callThe fields hold the text for the checkbox, back, and it is typically a function call the signaling method type, and the daeto a built-in routine or any method promon name (or complete command). vided by the programmer (the former in The script in Listing 8 begins with imthis case). Figure 1 shows what the report statements, followed by a method sulting window looks like (its appeardefinition, which Ill examine later. It ance will vary somewhat depending on then creates the main window, sets its the window manager). title, and adds some text:
Each line of the configuration file is split into the three-component field at the colon delimiters. All three are stored in successive elements of the dconfig list by the list append() method; the last character of the third field is dropped (the newline character). An integer variable is appended to the states list and initialized for use with Tkinter controls. Finally, a checkbutton is created for each entry with the configuration file label (the first field) as the button text. The resulting checkbox is controlled by a variable the corresponding element of states by which its value can be set, queried, or both. This section of the script ends by creating two buttons:
W W W. L I N U X - M A G A Z I N E . C O M
27
COVER STORY
one to signal the selected daemons, and one to exit the utility and start the event loop. The command argument to the first button deserves special attention for its use of the lambda function, which causes the specified method to be passed to the control rather than being executed immediately (try creating a control with a callback with and without this construct to understand its function). Figure 2 shows an example of how the resulting window looks. Listing 9 is the callback function (which would actually appear at the beginning of the script file). It examines the status array. For elements that are equal to 1 corresponding to elements whose boxes are checked it generates the appropriate command on the basis of the second type field: type i commands are started with the restart keyword to a script in /etc/init.d whose name is in the configuration files third field; type h
acommands are sent a HUP signal via the killall command with the name in the third field as its argument, and any other type uses the third field as the entire command to execute. The constructed command is executed by the os.popen() module, which executes an external shell command. The final act of the method is to reset the checkbox for each item. Note that this script does not do enough error checking for a production script, but it does illustrate many useful Python features.
interfacing to methods and programs in languages like C, C++, and Fortran straightforward. See the Info box for more information.
Exploring More
Ive only explored some basic Python features here, but you should be well prepared to learn more about Python on your own. Two of Pythons most useful features that I have not examined is classes and extensions written in other programming languages. Python makes both object-oriented programming and
INFO
[1] Syntax summary for this article: http://www.aeleen.com/python_summary.htm [2] Official Python website: http://www.python.org [3] Python Cookbook (examples): http:// aspn.activestate.com/ASPN/ Cookbook/Python [4] Instant Python by Magnus L. Hetland: http://www.hetland.org/python/ instant-python.php [5] Python Short Course by Richard P. Muller: http://www.wag.caltech.edu/ home/rpm/python_course
28
W W W. L I N U X - M A G A Z I N E . C O M