Understanding the Command Line
The command line is a text-based environment that some users never even see. You type a command and the computer follows it — nothing could be simpler. In fact, early PCs relied on the command line exclusively (even earlier systems didn’t even have a console and instead relied on punched tape, magnetic tape, punched cards, or other means for input, but let’s not go that far back). Some people are amazed at the number of commands that they can enter at the command line and the usefulness of those commands even today. A few administrators still live at the command line because they’re used to working with it. The following sections give you a better understanding of the command line and how it functions.
Newer versions of Windows (such as Vista and Windows 7) display a command prompt with reduced privileges as a security precaution. Many command line utilities require administrator privileges to work properly. To open an administrator command prompt when working with a newer version of Windows, right-click the Command Prompt icon in the Start menu and choose Run As Administrator from the context menu. You may have to provide a password to complete the command. When the command prompt opens, you have full administrator privileges, which let you execute any of the command line applications.
Understanding the Need for Command Line Applications
Many administrators today work with graphical tools. However, the graphical tools sometimes have problems — perhaps they’re slow or they don’t offer a flexible means of accomplishing a task. For this reason, good administrators also know how to work at the command line. A command line application can accomplish with one well-constructed command what a graphical application may require hundreds of mouse clicks to do — for example, the FindStr utility that lets you find any string in any file. Using FindStr is significantly faster than any Windows graphical search application and always provides completely accurate results. In addition, there’s that option of searching any file — many search applications skip executables and other binary files. Give it a try right now. Open a command prompt, change directories to the root directory (CD ), and type FindStr /M /S “Your Name“ and press Enter. You’ll find every file on the hard drive that contains your name.
In some cases, the administrator must work at the command line. If you’ve taken a look at Windows Server 2008 Server Core edition, you know that it doesn’t include much in the way of a graphical interface. In fact, this version of Windows immediately opens a command processor when you start it. There’s no desktop, no icons, nothing that looks even remotely like a graphical interface. In fact, many graphical applications simply don’t work in Server Core because it lacks the required DLLs. When faced with this environment, you must know how to use command line applications.
Don’t get the idea that command line applications are a panacea for every application ailment or every administrator need. Command line applications share some common issues that prompted the development of graphical applications in the first place. Here are the issues you should consider when creating a command line application of your own:
- Isn’t intuitive or easy to learn.
- Requires the user to learn arcane input arguments.
- Relies on the user to open a separate command prompt.
- Is error prone.
- Output results can simply disappear when starting the application without opening a separate command prompt.
Of course, you wouldn’t even be reading this chapter if command line applications didn’t also provide some benefits. In fact, command line applications are the only answer for certain application needs. Here are the benefits of using a command line application.
- Fast, no GUI to slow things down
- Efficient, single command versus multiple mouse clicks
- Usable in automation, such as batch files
- Less development time, no GUI code to write
- Invisible when executed in the background
Command line applications can have other benefits. For example, a properly written, general command line application can execute just fine on more than one platform. Even if you use .NET-specific functionality, there’s a very good chance that you can use an alternative, such as Mono (), to run your application on other platforms. Adding a GUI always complicates matters and makes your application less easy to move.
Reading Data from the Command Line
You have a multitude of options when working with data from the command line. Precisely which method you use depends on what you’re trying to achieve. If you merely want to see what the command line contains, you should use the Python approach because it’s fast and easy. However, Python doesn’t provide the widest range of command line processing features — it tends to focus on Unix methodologies. If you want additional flexibility in working with the command line options, you might use the .NET approach instead. The following sections describe both techniques.
Using the Python Method
Most programming languages provide some means of reading input from the command line and Python is no exception. As an IronPython developer, you also have full access to the Python method of working with the command line. While you’re experimenting, you may simply want to read the command line arguments. Listing 10-1 shows how to perform this task.
Listin g 10-1: Displaying the command line arguments
[code]
# Perform the required imports.
import sys
# Obtain the number of command line arguments.
print ‘The command line has’, len(sys.argv), ‘arguments.n’
# List the command line arguments.
for arg in sys.argv:
print arg
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
Developers who have worked with C or C++ know that the main() function can include the argc (argument count) and argv (argument vector — a type of array) arguments. Python includes the argv argument as part of the sys module. To obtain the argc argument, you use the len(sys.argv) function call. The example relies on a simple for loop to display each of the arguments, as shown in Figure 10-1.
Of course, you’ll want to expand beyond simply listing the command line arguments into doing something with them. Listing 10-2 shows an example of how you could parse command line arguments for the typical Windows user.
Listin g 10-2: Using the Python approach to parse command line arguments
[code]
# Perform the required imports.
import sys
import getopt
# Obtain the command line arguments.
def main(argv):
try:
# Obtain the options and arguments.
opts, args = getopt.getopt(argv, ‘Dh?g:s’, [‘help’, ‘Greet=’, ‘Hello’])
# Parse the command line options.
for opt, arg in opts:
# Display help when requested.
if opt in (‘-h’, ‘-?’, ‘–help’):
usage()
sys.exit()
# Tell the user we’re in debug mode.
if opt in (‘-D’):
print ‘Application in Debug mode.’
# Display a user greeting.
if opt in (‘-g’, ‘–Greet’):
print ‘Good to see you’, arg.strip(‘:’)
# Say hello to the user.
if opt in (‘-s’, ‘–Hello’):
print ‘Hello!’
# Parse the command line arguments.
for arg in args:
# Display help when requested.
if arg.upper() in (‘/?’, ‘/HELP’):
usage()
sys.exit()
# Tell the user we’re in Debug mode.
elif arg in (‘/D’):
print ‘Application in Debug mode.’
# Display a user greeting.
elif ‘/GREET’ in arg.upper() or ‘/G’ in arg.upper():
print ‘Good to see you’, arg.split(‘:’)[1]
# Say hello to the user.
elif arg.upper() in (‘/S’, ‘/HELLO’):
print ‘Hello!’
# User has provided bad input.
else:
raise getopt.GetoptError(‘Error in input.’, arg)
# The user supplied command line contains illegal arguments.
except getopt.GetoptError:
# Display the usage information.
usage()
# exit with an error code.
sys.exit(2)
# Call main() with only the relevant arguments.
if __name__ == “__main__“:
main(sys.argv[1:])
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
This example actually begins at the bottom of the listing with an if statement:
if __name__ == “__main__“:
main(sys.argv[1:])
[/code]
Many of your IronPython applications will use this technique to pass just the command line arguments to the main() function. As shown in Figure 10-1, the first command line argument is the name of the script and you don’t want to attempt processing it.
Python assumes that everyone works with Linux or some other form of Unix. Consequently, it only supports the short dash (–) directly for command line options. An option is an input that you can parse without too much trouble because Python does most of the work for you. Options use a single dash for a single letter (short option) or a double dash for phrases (long option). Anything that doesn’t begin with a dash, such as something that begins with a slash (/) is an argument. Unfortunately, most of your Windows users will be familiar with arguments, not options, so your application should process both.
The code begins by separating options and arguments that you’ve defined. The getopt.getopt() method requires three arguments:
- The list of options and arguments to process
- A list of short options
- A list of long options
In this example, argv contains the list of options and arguments contained in the command line, except for the script name. Each option and argument is separated by a space in the original string.
The list of short options is ‘Dh?g:s‘. Notice that you don’t include a dash between each of the options — Python includes them for you automatically. Each of the entries is a different command line switch, except for the colon. So, this application accepts –D, –h, –?, –g:, and –s as command line switches. The command line switches are case sensitive. The colon after –g signifies that the user must also provide a value as part of the command line switch.
The list of long options includes [‘help‘, ‘Greet=‘, ‘Hello‘]. Notice that you don’t include the double dash at the beginning of each long option. As with the short versions of the command line switch, these command line switches are case sensitive. The command line switches for this example are:
- –D: Debug mode
- –h, –?, and ––help: Help
- –g:Username and ––Greet:Username: Greeting that includes the user’s name
- –s and ––Hello: Says hello to the user without using a name
At this point, the code can begin processing opts and args. In both cases, the code relies on a for loop to perform the task. However, notice that opts relies on two arguments, opt and arg, while args relies on a single argument arg. That’s because opts and args are stored differently. The opts version of -g:John appears as [(‘–g‘, ‘:John‘)], while the args version appears as [‘/g:John‘]. Notice that opts automatically separates the command line switch from the value for you.
Processing opts takes the same course in every case. The code uses an if statement such as if opt in (‘–h‘, ‘–?‘, ‘––help‘) to determine whether the string appears in opt. In most cases, the code simply prints out a value for this example. The help routine calls on usage(), which is explained in the “Providing Command Line Help” section of the chapter. Calling sys.exit() automatically ends the application. If the application detects any command line options that don’t appear in your list of command line options to process, it raises the getopt.GetoptError() exception. Standard practice for Python applications is to display usage information using usage() and then exit with an error code (of 2 in this case by calling sys.exit(2)).
Now look at the args processing and you see something different. Python doesn’t provide nearly as much automation in this case. In addition, your user will likely expect / command line switches to behave like those for most Windows applications (case insensitive). The example handles this issue by using a different if statement, such as if arg.upper() in (‘/?‘, ‘/HELP‘). Notice that the options use a slash, not a dash.
Argument processing relies on a single if statement, rather than individual if statements. Consequently, the second through the last command line switches actually rely on an elif clause. Python won’t automatically detect errors in / command line switches. Therefore, your code also requires an else clause that raises the getopt.GetoptError() event manually.
Remember that arguments are single strings, not command line switch and value pairs. You need some method to split the command line switch from the value. The code handles this case using elif ‘/GREET‘ in arg.upper() or ‘/G‘ in arg.upper() where it compares each command line switch individually. In addition, it relies on arg.split(‘:‘)[1] to display the value. The argument processing routine shows that you can accommodate both Linux and Windows users quite easily with your application.
It’s time to test the example. Figure 10-2 shows the output of using IPY CmdLine2 .py –D –s –g:John /Hello /g:John.
Using the .NET Method
The .NET method of working with command line arguments is similar to the Python method, but there are distinct differences. When you design your application, you should use one technique of parsing the command line or the other because mixing the two will almost certainly result in application errors. Listing 10-3 shows a simple example of the .NET method.
Listin g 10-3: Using the .NET approach to list command line arguments
[code]
# Perform the required imports.
import System
# Obtain the number of command line arguments.
print ‘The command line has’,
print len(System.Environment.GetCommandLineArgs()),
print ‘arguments.n’
# List the command line arguments.
for arg in System.Environment.GetCommandLineArgs():
print arg
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
This example also relies on len() to obtain the number of command line arguments contained in System.Environment.GetCommandLineArgs(). As before, the code relies on a for loop to process the command line arguments. You might expect that the results would also be the same, but look at Figure 10-3 and compare it to Figure 10-1. Notice that the .NET method outputs not only the script name, but also the name of the script processor and its location on the hard drive. Using the .NET method can have benefits if you need to verify the location of IPY.EXE on the user’s system.
It’s time to see how you might parse a command line using the .NET method. Many of the techniques are similar, but there are significant differences because .NET lacks any concept of options versus arguments. In short, you use a single technique to process both in .NET. Listing 10-4 shows how to parse a command line using the .NET method.
Listin g 10-4: Using the .NET approach to parse command line arguments
[code]
# Perform the required imports.
from System import ArgumentException, Array, String
from System.Environment import GetCommandLineArgs
import sys
print ‘.NET Version Outputn’
try:
# Obtain the number of command line arguments.
Size = GetCommandLineArgs().Count
# Check the number of arguments.
if Size < 3:
# Raise an exception if there aren’t any arguments.
raise ArgumentException(‘Invalid Argument’, arg)
else:
# Create an array that has just command line arguments in it.
Arguments = Array.CreateInstance(String, Size – 2)
Array.Copy(GetCommandLineArgs(), 2, Arguments, 0, Size – 2)
# Parse the command line options.
for arg in Arguments:
# Display help when requested.
if arg in (‘-h’, ‘-?’, ‘/?’, ‘–help’) or arg.upper() in (‘/H’, ‘/HELP’):
usage()
sys.exit()
# Tell the user we’re in Debug mode.
elif arg in (‘-D’, ‘/D’):
print ‘Application in Debug mode.’
# Display a user greeting.
elif ‘-g’ in arg or ‘–Greet’ in arg or ‘/G’ in arg.upper() or
‘/GREET’ in arg.upper():
print ‘Good to see you’, arg.split(‘:’)[1]
# Say hello to the user.
elif arg in (‘-s’, ‘–Hello’) or arg.upper() in (‘/S’, ‘/HELLO’):
print ‘Hello!’
else:
raise ArgumentException(‘Invalid Argument’, arg)
except ArgumentException:
usage()
sys.exit(2)
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
The .NET implementation is a little simpler than the Python implementation — at least if you want to use both kinds of command line switches. This example begins by importing the required .NET assemblies. The example also relies on sys to provide the exit() function.
The code begins by checking the number of arguments. When using .NET parsing, you must have at least three command line arguments to receive any input. The example uses the ArgumentException() method to raise an exception should the user not provide any inputs.
In the IronPython example, the code uses a special technique to get rid of the script name. The .NET method also gets rid of the application name and the script name. In this case, the code creates a new array, Arguments, to hold the command line arguments. You must make Arguments large enough to hold all of the command line arguments, so the code uses the Array .CreateInstance() method to create an Array object with two fewer elements than the original array provided by GetCommandLineArgs(). The Array.CreateInstance() method requires two inputs: the array data type and the array length. The Array.Copy() method moves just the command line arguments to Arguments. The Array.Copy() method requires five inputs: source array, source array starting element, destination array, destination array starting element, and the number of elements to copy.
At this point, the code can begin parsing the input arguments. Notice that unlike the Python method, you can parse all the permutations in a single line of code using the .NET method. The example provides the same processing as the Python method example, so that you can compare the two techniques. As with the Python method, the .NET method raises an exception when the user doesn’t provide correct input. The result is that the example displays usage instructions for the application. Figure 10-4 shows the output from this example.
Providing Command Line Help
Your command line application won’t have a user interface — just a command. While some people can figure out graphical applications by pointing here and clicking there, figuring out a command line application without help is nearly impossible. The methods used to understand an undocumented command line application are exotic and usually require advanced debugging techniques, time spent in the registry, lots of research online, and more than a little luck. If you seriously expect someone to use your command line application, you must provide help.
Unlike a graphical application, you won’t need tons of text and screenshots to document most command line applications. All you really need is a little text that’s organized in a certain manner. Most command line applications use the same help format, which makes them easier to understand and use. However, not all command line applications provide all the help they really need. In order to provide your command line application with superior help, you need to consider the five following elements:
- Application description
- Application calling syntax
- Command line switch summary and description
- Usage examples
- (Optional) Other elements
The following sections describe all these elements and help you understand why they’re important. Of course, every command line application is different, so you’ll want to customize the suggestions in the following sections to meet your particular needs. The point is, you must provide the user with help of some kind.
Creating an Application Description
Many of the command line applications you see lack this essential feature. You ask for help and the application provides you with syntax and a quick overview of the command line switches. At the outset, you have little idea of what the application actually does and how the developer envisioned your using it. After a little experimentation, you might still be confused and have a damaged system as well.
An application description doesn’t have to be long. In fact, you can make it a single sentence. If you can’t describe your command line application in a single sentence, it might actually be too big — characteristically, command line applications are small and agile. Of course, there are exceptions and you may very well need an entire paragraph to describe your application. The big mistake is writing a huge tome. Most people using your application have worked with computers for a long time, so a shorter description normally works fine.
As a minimum, your application description should include the application name so the user can look for additional information online. The description should tell the user what the application does and why you created it.
Describing the Application Calling Syntax
Applications have a calling syntax — a protocol used to interact with the application. Unfortunately, you won’t have access to any formatting when writing your application help screen. Developers have come up with some methods to show certain elements over the years and you should use these methods for your command line syntax. Consider the following command line:
[code]
MyApp <Filename> [-S] [-s] [-D [-U[:<Name>]]] [-X | -Y | -Z | <Delta>] [-?]
[/code]
Believe it or not, all these strange looking symbols do have a meaning and you need to consider them for your application. Any item that appears in square brackets ([]), such as [–S], is optional. The user doesn’t need to provide it to use the application.
Anything that appears in angle brackets (<>), such as <Filename>, is a variable. The user replaces this value with some other value. Normally, you provide a descriptive name for the variable. For example, when you see <Filename>, you know that you need to provide the name of a file. In this case, <Filename> isn’t optional — the user must provide it unless asking for help. It’s understood that requesting help, normally –? or /?, doesn’t require any other input.
Command line switches within other command line switches are dependent on that command line switch. For example, you can use –D alone. However, if you want to use –U, you must also provide –D. In this case, –U is dependent on –D. Notice that you can use –U alone or you can include a <Name> variable with it. When you use the <Name> variable, the command line switch sequence must appear as –D –U:<Name>.
Sometimes a command line switch is mutually exclusive with other command line switches or even variables. For example, the [–X | –Y | –Z | <Delta>] sequence says that you can provide –X or –Y or –Z or <Delta>, but you can’t provide more than one of them.
Most Windows command line applications are case insensitive. However, there are notable exceptions to this rule. If you find that you must make your application case sensitive, be sure to use the correct case for the command line syntax. For example, –S isn’t the same as –s for this application and the command line syntax shows that. You should also note that the application is case sensitive in other areas of your help screen because some users won’t notice the difference in case.
Some developers will simply use [Options] for the command line syntax if you can use any of the command line switches at any time, or simply ignore them completely. There isn’t anything wrong with this approach, especially when your application defaults to showing the help screen when the user doesn’t provide any command line switches. However, make absolutely certain that your application truly doesn’t have a unique calling syntax before you use this approach.
Documenting the Command Line Switches
No matter how simple or complex the application, you need to document every command line switch. Most application writers use anywhere from one to three sentences to document the command line switch unless it’s truly complex. The command line switch documentation should focus on the purpose of the command line switch. Save any examples you want to provide for the usage examples portion of the help screen.
You must document every command line switch or the user won’t know it exists. Placing alternative command line switches together is a good idea because it reduces the complexity of the help screen. The order in which you place the command line switches depends on the purpose and complexity of your application. However, most developers use one of the following ordering techniques:
- Alphabetical: Useful for longer lists of command line switches because alphabetical order can make it easier to find a particular command line switch in the list.
- Syntactical: Developers especially like to see the command line switches in syntactical order. After viewing the syntax, the developer can find the associated command line switch description quickly.
- Order of potential usage: Placing the command line switches in order of popularity means that the user doesn’t have to search the entire list to find a particular command line switch description. This approach is less useful on long or complex lists because you really don’t know how the user will work with the application.
- Order of required use: In some cases, an application requires that a user place the command line switches in a particular order. For example, when creating a storyboard effect with a command line application, you want the user to know which command line switch to use first.
Some command line switch lists become quite long. In this case, you might want to group like command line switches together and place them in groups on the help screen. For example, you might have a set of command line switches that affects input and another that affects output. You could create two groups, one for each task, on your help screen to make finding a particular command line switch easier.
Showing Usage Examples
Most users won’t really understand your command line application unless you provide some usage examples. A usage example should show the command line and its result — if you do this, then you get that as output. Precisely how you put the examples together depends on your application and its intended audience. An application designed for advanced users can probably get by with fewer examples, while a complex application requires more examples. The usage examples should be non-trivial. You should try to show common ways in which you expect the user to work with your application.
Putting Everything Together
Now that you have a basic understanding of the required help screen elements, it’s time to look at an example. Listing 10-5 shows a typical usage() function. It displays help information to users who need it, using simple print() statements.
Listin g 10-5: Creating a help screen for your application
[code]
# Create a usage() function.
def usage():
print ‘Welcome to the command line example.’
print ‘This application shows basic command line argument reading.’
print ‘nUsage:’
print ‘tIPY CmdLine2.py [Options]‘
print ‘nOptions:’
print ‘t-D: Places application in debug mode.’
print ‘t-h or -? or –help: Displays this help message.’
print ‘t-g:<Name> or –Greet:<Name>: Displays a simple greeting.’
print ‘t-s or –Hello: Displays a simple hello message.’
print ‘nExamples:’
print ‘tIPY CmdLine2.py -s outputs Hello!’
print ‘tIPY CmdLine2.py -g:John outputs Good to see you John’
print ‘tYou can use either the – or / as command line switches.’
print ‘tFor example, IPY CmdLine2.py /s outputs Hello!’
[/code]
Notice the use of formatting in the code. The code places section titles at the left and an extra space below the previous section. Section content is indented so it appears as part of the section. Figure 10-5 shows the output from this code. Even though this help screen is quite simple, it provides everything needed for someone to use the example application to test command line switches.
Including Other Elements
Some command line application help screens become enormous and hard to use. In fact, some of Microsoft’s own utilities have help that’s several layers deep. Just try drilling into the Net utility sometime and you’ll discover just how cumbersome the help can become. Of course, you do want to document everything for the user. As an alternative, some command line application developers will provide an overview as part of the application, and then include a URL for detailed material online. It’s not a perfect solution because you can’t always count on the user having an Internet connection, but it does work most of the time.
You don’t have to stop with simple information redirection as part of your help. Some utilities include a phone number (just in case the user really is lacking that Internet connection). E‑mail addresses aren’t unusual, and some developers get creative in providing other helpful tips. It’s also important to take ownership of your application by including a company or developer name. If copyright is important, then you should provide a copyright notice as well. The thing is to make it easy for someone to identify your command line application without cluttering up the help screens too much.
To break the help screens up, you might want to include layered help. Typing MyApp /? might display an overview, while MyApp /MySwitch /? provides detailed information. Microsoft uses this approach with several of its utilities. If you use layered help, make sure you mention it on the overview help screen, or most users will think that the overview is all they get in the way of useful information.
Special settings require a section as well. For example, IPY.EXE provides access to some application features through environment variables. These environment variables appear in a separate section of the help screen.
Applications that could damage application data or the system as a whole in some way require warnings. Too few command line applications provide warnings, so command line applications have gotten a reputation for being dangerous — only experts need apply. The fact is that many of these applications would be quite easy to use with the proper warning information. However, don’t go too far in protecting the user by providing messages that request the user confirm a particular task. Using confirmations would reduce the ability of developers to use the command line applications for batch processing and automation needs.
Given that your application might inadvertently damage something when the user misuses it, you might also want to include fixes and workarounds as part of your help. Unfortunately, it’s the nature of command line utilities that the actions they perform are one-way — once done, you can’t undo them.
Interacting with the Environment
The application environment consists of a number of elements. Of course, you need to consider whether the application uses a character mode interface or a graphical interface. The platform on which the application runs is also a consideration. Depending on the application’s purpose, you may need to consider background task management as part of the picture. Most developers understand that these elements, and more, affect the operation of the application. However, some developers miss out on a special environmental feature, the environment variable. Using environment variables makes it possible to communicate settings to your application at a number of different levels in a way that command line switches can’t. In fact, you may not even realize it, but there are several different levels of environment variables with which you can control an application, making the variables quite flexible. The following sections describe environment variables and their use in IronPython.
Understanding Environment Variables
Environment variables are simply a kind of storage location managed by the operating system. When you open a command prompt, you can see a list of environment variables by typing Set and pressing Enter. Figure 10-6 shows the environment variables on my system. The environment variables (or at least their values) will differ on your machine, so you should take a look at them. If you want to see the value of a particular environment variable, type Set VariableName (such as Set USERNAME) and press Enter. To remove an environment variable, simply type Set VariableName= (with no value) and press Enter. (Never remove environment variables you didn’t create because some of your applications could, or more likely will, stop working.)
As you can see from Figure 10-6, environment variables appear as a name/value pair. An environment variable with a specific name has a certain value. Some environment variables in this list are common to all Windows machines. For example, the system wouldn’t be able to find applications without the Path environment variable. Environment variables such as COMPUTERNAME and USERNAME can prove helpful for your applications. You can also discover facts such as the processor type and system drive using environment variables.
It’s possible to create environment variables using a number of techniques. However, the method used to create the environment variable determines its scope (personal or global), visibility (command prompt only or command prompt and Windows application), and longevity (session or permanent). For example, if you type Set MyVar=Hello (notice that there are no quotes for the value) and press Enter, you create a personal environment variable that lasts for the current session and is visible only in the command prompt window. You can see any environment variable by typing Echo %VarName% and pressing Enter. Try it out with MyVar. Type Echo %MyVar% and press Enter to see the output shown in Figure 10-7.
The most common way to set a permanent environment variable is to click Environment Variables on the Advanced tab of the System Properties dialog box. You see the Environment Variables dialog box shown in Figure 10-8. This dialog box has two environment variable settings areas. The upper area manages personal settings that affect just one person — the current user. The lower area manages environment variables that affect everyone who uses the system.
To create a new environment variable, simply click New. You see the New User Variable (shown in Figure 10-9) or the New System Variable dialog box. In both cases, you type an environment variable name in the Variable Name field and an environment variable value in the Variable Value field. Click OK and you see the environment variable added to the appropriate list. Editing an environment variable is just as easy. Simply highlight the environment variable you want to change in the list and click Edit. You’ll see a dialog box similar to the one shown in Figure 10-9 where you can change the environment variable value. To remove an environment variable, simply highlight its entry in the list and click Delete.
Any changes you make to environment variables won’t show up until you close and reopen any command prompt windows. Windows provides the current set of environment variables to every command prompt window when it opens the window, but it doesn’t perform updates.
The interesting thing about environment variables you set using the Environment Variables dialog box is that they are also available to Windows applications. You can read these environment variables just as easily in a graphical application as you can in a character mode application
You may find that you want to create environment variables for just the command prompt. Of course, you can always use the Set command approach described earlier in this section. However, most developers will want something a little more automated. If you need to set command line–only environment variables for the entire machine, then you need to modify the AutoExec.NT file found in the WINDOWSsystem32 folder of your system. Figure 10-10 shows a typical view of this file.
Simply open the file using a text editor, such as Notepad (don’t use WordPad), and add a Set command to it. Every time someone opens a command prompt, Windows reads this file and uses the settings in it to configure the command prompt window. Many people forget that the AutoExec.NT file even exists, but it’s a valuable way to add Set commands in certain cases.
It’s also possible to set individualized command prompt environment variables for a specific application. In this case, create a batch (.BAT) file using a text editor. Add Set commands to it for the application, and then add a line to start the application, such as IPY MyApp.py. In short, you can make environment variables appear whenever and wherever you want by simply using the correct method to create them.
Using the Python Method
Python provides operating system–generic methods of reading and writing variables. As with many things in IronPython, the Python techniques work great across platforms, but probably won’t provide the greatest flexibility. The following sections describe the techniques you use to read and set environment variables using the Python method.
Reading the Environment Variables Using Python
This example looks at a new Python module, os, which contains a number of interesting classes. In this case, you use the environ class, which provides access to the environment variables and lets you manipulate them in various ways, as shown in Listing 10-6.
Listin g 10-6: Displaying the environment variables using the Python method
[code]
# Import the required Python modules.
import os
# Obtain the environment variable keys.
Variables = os.environ.keys()
# Sort the keys in alphabetic order.
Variables.sort()
# Display the keys and their associated values.
for Var in Variables:
print ‘%30s %s’ % (Var,os.environ[Var])
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
The code begins by importing the required modules, as normal. It then places the list of environment variable keys, the names, in Variables using os.environ.keys(). In most cases, you want to view the environment variables in sorted order because there are too many of them to simply peruse a list, so the code sorts the list using Variables.sort().
At this point, the code is ready to display the list. It uses a simple for loop to perform the task. Notice the use of formatting to make the output more readable. Remember that the values don’t appear in the Variables list, so you must obtain them using os.environ[Var]. Figure 10-11 shows typical output from this example.
Setting the Environment Variables Using Python
Python makes it relatively easy to set environment variables. However, the environment variables you create using IronPython affect only the current command prompt session and the current user. Consequently, if you start another application in the current session (see the section “Starting Other Command Line Applications” later in the chapter for details), it can see the environment variable, but if you start an application in a different session or start a graphical application, the environment variable isn’t defined. In addition, changes you make to existing environment variables affect only
the current session. Nothing is permanent. Listing 10-7 shows how to modify environment variables using the Python method.
Listin g 10-7: Setting an environment variable using the Python method
[code]
# Import the required Python modules.
import os
# Create a new environment variable.
os.environ.__setitem__(‘MyVar’, ‘Hello’)
# Display its value on screen.
print ‘MyVar =’, os.environ[‘MyVar’]
# Change the environment variable and show the results.
os.environ.__setitem__(‘MyVar’, ‘Goodbye’)
print ‘MyVar =’, os.environ[‘MyVar’]
# Delete the variable, and then try to show it.
try:
os.environ.__delitem__(‘MyVar’)
print ‘MyVar =’, os.environ[‘MyVar’]
except KeyError as (KeyName):
print ‘Can‘t display’, KeyName
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
Setting and changing an environment variable use the same method, os.environ.__setitem__(). In both cases, you supply a name/value pair (MyVar/Hello). When you want to see the value of the environment variable, you request the value by supplying the name, such as os.environ[‘MyVar‘] for this example.
Deleting an environment variable requires use of os.environ.__delitem__(). In this case, you supply only the name of the environment variable you want to remove.
If you try to display an environment variable that doesn’t exist, the interpreter raises a KeyError exception. The example shows the result of trying to print MyVar after you remove it using os.environ.__delitem__(). Figure 10-12 shows the output from this example.
Using the .NET Method
Working with environment variables using the .NET method isn’t nearly as easy as working with them using the Python method. Then again, you can make permanent environment variable changes using .NET. In fact, .NET provides support for three levels of environment variables.
- Process: Affects only the current process and any processes that the current process starts
- User: Affects only the current user
- Machine: Affects all users of the host system
An important difference between the Python and .NET methods is that any change you make using the .NET method affects both command line and graphical applications. You have significant control over precisely how and where an environment variable change appears because you specify precisely what level the environment variable should affect. The following sections provide more information on reading and setting environment variables using the .NET method.
Reading the Environment Variables Using .NET
As previously mentioned, the .NET method is more flexible than the Python method, but also requires a little extra work on your part. Some of the extra work comes in the form of flexibility. The .NET method provides several ways to obtain environment variable data.
- Use one of the Environment class properties to obtain a standard environment variable value. You can find a list of these properties at system.environment_properties.aspx.
- Check a specific environment variable using GetEnvironmentVariable().
- Obtain all the environment variables for a particular level using GetEnvironmentVariables() with an EnvironmentVariableTarget enumeration value.
- Obtain all the environment variables regardless of level using GetEnvironmentVariables().
It’s important to note that these techniques let you answer questions such as whether a particular environment variable is a standard or custom setting. You can also determine whether the environment variable affects the process, user, or machine as a whole. In short, you obtain more information using the .NET method, but at the cost of additional complexity. Listing 10-8 shows how to read environment variables using each of the .NET methods.
Listin g 10-8: Displaying the environment variables using the .NET method
[code]
# Obtain access to Environment class properties.
from System import Environment
# Obtain all of the Environment class methods.
from System.Environment import *
# Import the EnvironmentVariableTarget enumeration.
from System import EnvironmentVariableTarget
# Display specific, standard environment variables.
print ‘Standard Environment Variables:’
print ‘tCurrent Directory:’, Environment.CurrentDirectory
print ‘tOS Version:’, Environment.OSVersion
print ‘tUser Name:’, Environment.UserName
# Display any single environment variable.
print ‘nSpecific Environment Variables:’
print ‘tIronPython Path:’, GetEnvironmentVariable(‘IronPythonPath’)
print ‘tSession Name:’, GetEnvironmentVariable(‘SessionName’)
# Display a particular kind of environment variable.
print ‘nUser Level Environment Variables:’
for Var in GetEnvironmentVariables(EnvironmentVariableTarget.User):
print ‘t%s: %s’ % (Var.Key, Var.Value)
# Display all of the environment variables in alphabetical order.
print ‘nAll of the environment variables.’
# Create a list to hold the variable names.
Keys = GetEnvironmentVariables().Keys
Variables = []
for Item in Keys:
Variables.Add(Item)
# Sort the resulting list.
Variables.sort()
# Display the result.
for Var in Variables:
print ‘t%s: %s’ % (Var, GetEnvironmentVariable(Var))
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
The code begins by importing some .NET assemblies. Notice that the example reduces clutter by importing only what the code actually needs.
As mentioned earlier, you can obtain standard environment variable values by using the correct property value from the System.Environment class. In this case, the code retrieves the current directory, operating system version, and the user name, as shown in Figure 10-13.
The next code segment in Listing 10-8 shows how to obtain a single environment variable. All you need is the GetEnvironmentVariable() with a variable name, such as, IronPythonPath.
If you want to work with the environment variables found at a particular level, you use GetEnvironmentVariables() with an EnvironmentVariableTarget enumeration value, as shown in the next code segment in Listing 10-8. Unless you create a custom environment variable, you won’t see any output at the EnvironmentVariableTarget.Process level.
You might remember from Listing 10-6 the ease of sorting the environment variables when using the Python method. Sorting the environment variables when using the .NET method isn’t nearly as easy because the .NET method relies on a System.Collections.Hashtable object for the output of the GetEnvironmentVariables() method call. The easiest method to sort the environment variables is to obtain a list of the keys using GetEnvironmentVariables() .Keys, the Keys object; place them in a list object, Variables; and then sort as normal using Variables.sort().
Now that the code has a sorted list, it uses a for loop to enumerate each environment variable using GetEnvironmentVariable(). Figure 10-13 does show the entire list, but when you try the example, you’ll see that the list is indeed sorted. There are definitely times where .NET objects will cause problems for your IronPython application and this is one of them.
Setting the Environment Variables Using .NET
The .NET method provides some additional setting capabilities when compared to the Python method. For one thing, you can make the environment variable settings permanent. The reason for this difference is that the .NET method lets you write the settings directly to the registry. You won’t manipulate the registry directly, but the writing does take place in the background, just as it would if you used the Environment Variables dialog box.
You do have some limitations. For example, you can’t change an Environment class property value. This restriction makes sense because you don’t want to change an environment variable that a number of applications might need. Listing 10-9 shows how to set environment variables as needed.
Listin g 10-9: Setting an environment variable using the .NET method
[code]
# Obtain access to Environment class properties.
from System import Environment
# Obtain all of the Environment class methods.
from System.Environment import *
# Import the EnvironmentVariableTarget enumeration.
from System import EnvironmentVariableTarget
# Create a temporary process environment variable.
SetEnvironmentVariable(‘MyVar’, ‘Hello’)
print ‘MyVar =’, GetEnvironmentVariable(‘MyVar’)
# Create a permanent user environment variable.
SetEnvironmentVariable(‘Var2’, ‘Goodbye’, EnvironmentVariableTarget.User)
print ‘Var2 =’, GetEnvironmentVariable(‘Var2’)
print ‘Var2 =’, GetEnvironmentVariable(‘Var2’, EnvironmentVariableTarget.User)
raw_input(‘nOpen the Environment Variables dialog box…’)
# Delete the temporary and permanent variables.
print ‘nDeleting the variables…’
SetEnvironmentVariable(‘MyVar’, None)
SetEnvironmentVariable(‘Var2’, None, EnvironmentVariableTarget.User)
print ‘MyVar =’, GetEnvironmentVariable(‘MyVar’)
print ‘Var2 =’, GetEnvironmentVariable(‘Var2’, EnvironmentVariableTarget.User)
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
The example begins with the usual assembly imports. It then creates a new environment variable using the SetEnvironmentVariable() method. If you call SetEnvironmentVariable() without specifying a particular level, then the .NET Framework creates a temporary process environment variable that only lasts for the current session.
The next step creates a permanent user environment variable. In this case, you must supply an EnvironmentVariableTarget enumeration value as the third argument. This portion of the example also demonstrates something interesting. If you create a new permanent environment variable in a process, the .NET Framework won’t update that process (or any other process for that matter). Consequently, the first call to GetEnvironmentVariable() fails, as shown in Figure 10-14.
To see the environment variable, you must either restart the process or you must call GetEnvironmentVariable() with an EnvironmentVariableTarget enumeration value. As a result, the second call succeeds. At this point, the example pauses so you can open the Environment Variables dialog box and see for yourself that the environment variable actually does exist as a permanent value.
Deleting an environment variable is as simple as setting it to None using the SetEnvironmentVariable() method. However, you need to delete permanent environment variables by including the EnvironmentVariableTarget enumeration value, or the .NET Framework won’t delete it. Unlike the Python method, you won’t get an error when checking for environment variables that don’t exist using the .NET method. Instead, you’ll get a value of None, as shown in Figure 10-14.
Environment Variable Considerations
Some developers don’t think too hard about how the changes they make to the environment will affect other applications. One application, which will remain nameless, actually changed the path environment variable and caused other applications to stop working. Users won’t tolerate such behavior because it impedes their ability to perform useful work. In addition, companies lose a lot of money when administrators have to devote time to fixing such problems.
The standard rules for using environment variables is that you should only read environment variables created by others. You may find a situation where you need to change a non-standard environment variable, but proceed with extreme caution. It’s never allowed to change a standard environment variable, such as USERNAME, created by the operating system because doing so can cause a host of problems.
If you want to have an environment variable you can change, create a custom environment variable specifically for your application. Even if you have to copy the value of another environment variable into this custom environment variable, you can be sure you won’t cause problems for other applications if you always use custom environment variables for your application.
Starting Other Command Line Applications
You can start other applications using IronPython. In fact, Python provides a number of techniques for performing this task. If you’ve worked with a .NET language for a while, you know that the .NET Framework also provides several methods of starting applications. However, most developers want to do something simple with the applications they start as subprocesses. For example, you might want to get the operating system to perform a task that IronPython won’t perform for you directly.
IronPython sports a plethora of methods to execute external applications. However, the simplest of these methods is os.popen(). Using this method, you can quickly open an external application, obtain any output it provides, and work with that output in your application. These three steps are all that many developers need. Listing 10-10 shows how to use os.popen() to execute an external application.
Listin g 10-10: Starting applications directly in IronPython
[/code]
# Import the required module.
import os
# Open a copy of Notepad.
os.popen(‘Notepad C:/Test.TXT’)
# Use the Dir command to get a directory listing and display it.
Listing = os.popen(‘Dir C:\ /OG /ON’)
for File in Listing.readlines():
print File,
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]
This example begins by opening a copy of Notepad with C:/Test.TXT. Notice that the command uses a slash, not a backslash. In many cases, you can use a standard slash to avoid having to use a double backslash (\) in your command. When this command executes, you see a copy of Notepad open with the file loaded. Of course, you need to create C:/Test.TXT before you execute the example to actually see the file loaded into Notepad.
In some cases, you need to read the output from a command after it executes. For example, you might want to obtain a directory listing using particular command line switches. The second part of the example shows how to perform this task. When the Dir command returns, Listing 10-10 has a directory listing in it similar to the one shown in Figure 10-15. In this case, you must provide the double backslash because, for some reason, Dir won’t work with the / when called from IronPython.
If you really need high-powered application management when working with IronPython, then you want to use the subprocess module, which contains a single method, Popen(). This approach is for those few who really need extreme control over the applications they execute. You can read about this module at. The os module also has a number of popen() versions, ranging from popen() to popen4(). Generally, if popen() won’t meet your needs, it’s probably a good idea to use the subprocess .Popen() method because it provides better support for advanced functionality.
Providing Status Information
Your administrative application often performs tasks without much user interaction, which means that the user might not even be aware of errors that occur. Consequently, you need to provide some means of reporting status information. The following sections provide a quick overview of some techniques you can use to report status information to the user.
Reporting Directly to the User
The time honored method of reporting status information to the user is to display it directly onscreen. In fact, most of the applications in this book use this approach. If you know that the user will be watching the display or at least checking it from time-to-time, it’s probably a good idea to provide direct information. Make sure you provide all the details, including error numbers and strings as appropriate. Depending on the skill of the user, you’ll want to provide messages that are both friendly and easy to understand. Otherwise, less-skilled users are apt to do something rash because they don’t understand what the message is telling them.
If you know that less skilled users will rely on your application, you should provide a secondary method of reporting status information such as an event log. Log files are also helpful, but can prove troublesome for the administrator to access from a remote location. The Microsoft Management Console (MMC) provides easy methods for administrators to gain access to remote event logs as necessary.
You can probably provide a remote paging system or similar contact techniques for the administrator as well. However, such methods are somewhat complex and not directly supported by IronPython through the Python libraries. The implementation of these techniques is outside the scope of this book. However, you’ll probably want to use a .NET Framework methodology, such as the one described at, to perform this task.
Creating Log Files
At one time, administrators relied on text log files to store information from applications. However, most applications today output complex information that’s hard to read within a text file. If you plan to create log files for your application, you probably want to store them in XML format to make them easy to ready and easy to import into a database.
Using the Event Log
Many applications rely on the event log as a means to output data to the administrator. Of all of the methods that Microsoft has created for outputting error and status information, the event log has been around the longest and is the most successful. Fortunately, for the IronPython developer, using the event log is extremely easy and it’s the method that you should use most often. Listing 10-11 shows just how easy it is to write an event log entry.
Listin g 10-11: Writing an event log entry
[code]
# Import the required assemblies.
from System.Diagnostics import EventLog, EventLogEntryType
# Create the event log entry.
ThisEntry = EventLog(‘Application’, ‘Main’, ‘SampleApp’)
# Write data to the entry.
ThisEntry.WriteEntry(‘This is a test!’, EventLogEntryType.Information)
# Pause after the debug session.
raw_input(‘Event log entry written…’)
[/code]
The EventLog() constructor accepts a number of different inputs. The form shown in the example defines the log name, machine name, and the application name. In most cases, this is all the information you need to start writing event log entries.
After you create ThisEntry, you can use it to begin writing event log entries as needed using the WriteEntry() method. The WriteEntry() is overloaded to accept a number of information formats — the example shows what you’ll commonly use for simple entries. You can see other forms of the WriteEntry() method at .eventlog.writeentry.aspx.
In this case, the WriteEntry() provides a message and defines the kind of event log entry to create. You can also create warning, error, success audit, and failure audit messages. Figure 10-16 shows the results of running this example.
