Linux.com
Everything Linux and Open Source
How to write a thorough review of a Linux distribution
July 03, 2008 (9:00:00 PM) - 1 week ago
By: Mark Gregson
I have never written a review of a Linux distribution, but I've read more than I can count, and many of them have been maddeningly incomplete and not worth the time it took to read them. Here's a list of items you need to talk about in order to write a thorough review, covering every aspect of the distribution from the initial download to the final recommendation and everything in between.
Not every item below applies to every distribution; you need to choose which items to include and which to ignore. For example, if the distribution is for an embedded device, there's probably not much point in discussing window manager themes. However, the more you include, the better your review will be. You can cover some of this information in a simple table, but many of the points deserve more explanation.
In addition to talking about each item, you should tell your readers how important or useful you think each item is. For example, if the distribution automatically boots all the way to a logged-in guest account, do you like this or not?
Purpose of distribution
Describe the purpose of the distribution as given by the creators. For example, the distribution may be intended for servers or it might instead be for multimedia creators. If the creators do not list a purpose, you can give your view, but this should be done in a later section where you describe your impressions and recommendations.
Parent distribution
Describe the "parent" distribution this distribution is based on. For example, is this distribution Slackware or Debian based? If it is not based on a parent distribution, discuss the implications of this. Spend more time on this point if the parent distribution is particularly relevant -- for example, is the distribution intended to be minimalist but was built on the biggest kernel around?
Version
Give the version number of the distribution you are reviewing. What is the release date? Is this the latest version? If not, explain why you are you reviewing an older version. For example, the latest version might be beta and you only want to review the latest stable release. Which kernel is the distribution based on? Does the distribution contain multiple kernel versions? Don't just list the kernel as "2.6" but rather give the minor release number also (e.g. 2.6.25). Note whether the kernel is recent or older and discuss the distribution release dates versus the kernel release dates if that is relevant. For example, if the distribution is only days or weeks old but the kernel version is much older, note this. If the distribution uses a special kernel, include the bug fix number (e.g. 2.6.16.28).
Distribution creators
Mention the distribution creators -- is it one person working out of his home or is the distribution from a large scientific or government body?
Acquiring
Describe the methods provided to get the distribution. List the size of the distribution. Are there mirrors in major regions or is the distribution only available on its Web site? How fast was the download? How did you find out about this distribution? Are there other formats available? For example, can one purchase a DVD or CD? Is this a commercial or a free release?
CPU architectures
List the CPU architectures the version supports. Tell the readers whether one download supports multiple architectures, and about any special architectures that are supported.
Live vs. install
Say whether the distribution requires an install before you can use it fully. Most distributions have a live CD version. List the various "flavours" the distribution comes in. For example, are there separate minimal and full versions? Are there versions for different kinds or users or for different computer architectures?
Live CD issues
Describe the distribution's approach to saving personal files while in live CD mode. Are there any ways of saving the configuration? Does the distribution have a control panel entry for this? How easy is it to restart the live CD version and apply your saved configuration?
Install
Discuss the tools and process of installing the distribution onto the hard drive. How much manual intervention is required? How much technical expertise is needed? How well is the process documented? Does the system automatically partition the drives? Does it automatically backup existing files? Which filesystems are supported? Is there a default filesystem?
Languages
Since you will likely write your review in English (since you are reading this guide in English) then if the default language is not English emphasize the point. List the languages supported by the distribution. What is the default language? How well supported are the other listed languages? For example, if English is not the default language, do applications and documentation switch to English when you switch the language to English?
Boot issues
Describe which boot parameters are required and which boot options are available. Does the distribution stop and ask for user input or does it boot automatically? Even if it does boot all the way to a graphical desktop, does it still require configuration settings in the desktop? For example, some distributions will run all the way to a logged-in user running X but then show a dialog box requiring the person to set up the network card.
Describe the ease of modifying the boot parameters, especially if you had to change any of the defaults in order to get the distribution working on your machine. Were there function keys that changed the default resolution or runlevel? Describe the level of documentation provided on the boot screens themselves. For example, did you have to already know that you should type "noapci" or did the boot screens explain all that and all other "cheat codes" (at least the ones required to get your machine working)?
Tell the user if the system boots with a splash screen. This is particularly important if the distribution is intended for novice users. Does the distribution show any boot output or is it fully hidden?
Start scripts
Describe the startup scripts that come with the distribution. Besides startx, are there separate start scripts for each window manager? How easy is it to find them (i.e. are they documented clearly)? Are there start scripts for services like CUPS and firewalls?
Graphical vs. text
Explain what happens when the system has finished booting. Does it automatically start an X session? Which version of X does it use? If it doesn't start X automatically, what does it do? What sorts of instructions are given on screen, regardless of the boot mode? Is there enough information given to tell the user how to log in? For example, for any distribution intended for novice users, are the username and password shown on the login screen?
Screen resolution
Discuss the video mode the system boots to by default if it boots to X. Does it automatically find and use the highest resolution or possibly the highest refresh rate? Is the boot video mode selectable at some point?
Hardware detection
Describe the automatic hardware detection of the distribution. This includes but is not limited to video cards, audio cards, keyboards, mice (including the scroll wheel), USB ports, hard drives, CD and DVD devices, modems, network cards, printers, and scanners. Are the correct video drivers loaded? If you plug in or remove USB devices, does the system correctly configure the devices? Does your sound card work? Which sound architecture does it use?
Mounting
Tell whether the hard drives are mounted in read-only or in read-write mode. Can the distribution read and write non-Linux drives? How easy is it to switch between read-only and read-write? Were all the hard drives automatically mounted?
Network
Discuss how the distribution configures the network devices. Are they all discovered? Does the system let you use DHCP? How much manual configuration is required? What tools are provided for managing the network?
Describe whether wireless network cards are properly configured. On startup, does the system tell you whether it tried, and what the result is? Are there any special tools to help you manage wireless connections?
Printing
Describe the printer setup process. Print some documents. Is CUPS automatically started? How easy is it to add a printer? Which printers are supported?
Laptop support
If you run the distribution on a laptop, discuss how well the hardware is supported. Does the distribution correctly deal with putting the laptop into sleep and hibernate modes? Is the infrared port configured? Does it support Bluetooth?
Login manager
Mention the login manager that starts by default. You can also list other supplied login managers, if any.
Window managers
List all the window managers supplied with the distribution and their version numbers. Does the login manager (if any) list the window managers and allow the user to select one different from the default? Are there window managers included that aren't shown by the login manager? Are there any unusual window managers?
Themes
Discuss the themes provided. How easy is it to change themes and get new ones?
Look and feel
Discuss the overall look of the distribution. Is there a consistent feel starting from the boot loader and going all the way through the login manager, default themes, and any customized control panels?
Fonts
Discuss the fonts that are installed with the distribution. Note anything exceptional, such as a large number of unusual fonts.
Desktop
Describe the desktop. Which applets are started and in which taskbars? What icons are there? Are there any special panels or menus? Do any other programs start automatically?
Menus
Describe how well the menus are configured in each window manager. Does each window manager have the same applications listed as the others, and are they laid out identically to the other supplied window managers as far as the window manager will allow? Are all the important installed applications listed? Are there submenus by category? If so, how well do the applications fit the assigned category? How many submenus do you need to navigate before you find what you want? Are there desktop icons for any applications? Do not assume that all applications shown in menus actually work or are even installed. For example, if the distribution includes a word processor in the menu, did you start it?
Software selection
Discuss the applications that are installed in the distribution by category. Highlight any unusual applications. List any non-open-source applications. Note especially if anything is missing. For example, if this is a desktop distribution, are there office applications? If it is a server edition, are all the services available?
Codecs
Discuss the codecs supplied with the distribution. How complete is the set of included codecs? Are all the free codecs included by default? Are there non-free codecs that might cause legal issues in some jurisdictions? How easy is it to get additional codecs if required? How well did the applications play the media you tested?
Customized control panel
Describe any customized control panels that come with the distribution. What tasks do they cover? How easy are they to find and use? Do the tools work completely or does the user need to do further work via non-customized tools?
Customized application configuration
Describe any customized application configuration tools provided. For example, is there a customized tool to configure encryption?
Special configurations
Describe any special programs and configurations not found in most other distributions. For example, if the distribution includes Wine, is it fully configured to run, and are there any installed Windows applications that are preconfigured? Are there icons or menu items that start these applications?
Development tools
Describe the development tools provided. List the compilers, editors, and integrated development environments.
Services
List the services that are started automatically. For example, is sshd started? Describe the system for controlling the starting and stopping of services. Is there an administration control panel for services? Are any important services not included in the control panel?
Security
Describe the security features of the distribution. Is the root password weak or strong? Are there any guest accounts or does the distribution automatically log in as root? Are the guest accounts password protected? Does the distribution boot all the way to a desktop or is there a login screen? Are the passwords easy to find? For example, some distributions show the password on the login screen as part of the wallpaper. Are there special security features such as firewalls or automatically encrypted filesystems?
Packages
Discuss package management for the distribution. Test out some package installs. Which package repositories are preconfigured? How many packages are there for this distribution? What is the package manager? How easy was it to install applications? Did the newly installed packages work?
Patches
Describe the method provided by the distribution creators for patching the distribution. For example, are there any patches or does the user need to download a complete copy of the latest distribution? How easy is it to apply the patches?
Performance
Discuss the speed of the distribution if it differs significantly from other distributions. Does it boot extra quickly? Is the windowing system extremely responsive? Is the kernel optimized in any way that shows? Does the distribution run entirely within RAM? Did the install go extremely slow?
Stability
Describe the stability of the distribution. Do any of the applications crash? Does the distribution start the same every time? Does it have trouble with certain configurations of hardware such as laptops or old computers?
Shutdown
Describe what happens when the user exits. For example, if you shut down X via the Ctrl-Alt-Backspace key combination, do you return to a login screen or does the system shut down? Does it drop the user to a command line? Does the system power off automatically? Does it eject the CD during shutdown?
Remastering
Discuss any tools provided for rebuilding the distribution. How easily can you remaster the distribution? Is there any documentation for this task?
Upgrade and rollback
Discuss the process of moving to a newer or older version of the distribution after having done an install. Are there any tools provided to assist with this?
Problems
List all the problems you encountered. Discuss what you found confusing. What did not work as documented? What workarounds did you have to apply? Did the solutions provided by the official support staff or the user community work? Are problems noted on the Web site, and does the project have an expected schedule of when they will be fixed?
Documentation
Describe the documentation provided with the distribution. Are there man pages for every application? Is the info command included with full documentation? Does the distribution include the help files for each application? Are all the customized tools documented?
Support
Describe the official support for the distribution. How responsive are the creators to user questions? Are they open to receiving help? How complete is the Web site, and how easy is it to find help there? Are there other help forums, such as IRC channels, and do developers frequent them?
User community
Describe the user community to the extent you can judge from the distribution's forums. How large and active is it? How helpful is it? How friendly is it?
Source code availability
Describe the process of getting the software for the distribution. How easy is it to find all the software, especially for the customized tools? How well is it documented?
Releases and roadmap
Does the distribution have a regular release schedule? Is the schedule published? How closely do the creators adhere to the schedule? Describe the roadmap for the distribution. Are there documented plans for future releases? What things are in the roadmap?
History
Describe the history of the distribution. Has the distribution been stable at each release? How has the distribution changed over time? When did the distribution first appear and how often was it updated? Has the distribution been influential on other distributions or in other ways?
Screenshots
Provide useful screenshots, not just screenshots for the sake of having some pictures in your review. Useful screenshots mean noteworthy items described in the text, such as something that makes the distribution distinctive or anything particularly interesting.
Links
Provide links in your review not only to the distribution Web site but also to anything that is not directly related to the distribution you are reviewing. For example, if you mention other distributions you should include links to them.
Your omissions
List the things you did not test and the reasons why you did not.
Your system and your perspective
Describe the hardware and software configuration of your test machine. Did you have any esoteric hardware? Did you run the distribution inside a virtual machine?
Describe your relevant experience. How long have you been using Linux? How many distributions have you tried? Which distributions do you regularly use? Are you an administrator or programmer?
Discuss your reasons for reviewing this distribution. For example, do you want to describe the latest release of this distribution? Do the distribution creators make any special claims for this release or this distribution? Have you been using this distribution for day-to-day work?
Disclose your biases. For example, do you prefer everything to be automated or would you rather have manual control? Do you have a favourite application that wasn't included when you thought it should be?
Recommendations
Summarize your review and your impressions. Compare this distribution against others of the same type. If the target audience is described by the creators, does the distribution meet the needs of the target audience? What kind of users will most like this distribution?
Final words
Consider having your review edited. Your review should be well-organized, clearly worded, and without grammar or spelling errors.
This guide on writing reviews means that you won't be able to blast through a distribution in 20 minutes, but the extra time you take to cover so many items will be rewarded by your readers' interest and appreciation. They'll want to read the next thing you write. Your review may become the definitive standard against which all others are compared.
Read in the original layout at: http://www.linux.com/feature/139593
Thursday, July 10, 2008
How to write a thorought review of a linux distribution
Wednesday, July 9, 2008
Beyond Your First Shell Script
August 1st, 1996 by Brian Rice in
* Software
As your shell scripts get more complex, you'll need to put a directive at the beginning to tell the operating system what sort of shell script this is.
So here it is—your first shell script:
lpr weekly.report
Mail boss < weekly.report
cp weekly.report /floppy/report.txt
rm weekly.report
You found yourself repeating the same few commands over and over: print out your weekly report, mail a copy to the boss, copy the report onto a floppy disc, and delete the original. So it was a big time saver when someone showed you that you can place those commands into a text file (“dealwithit”, for instance), mark the file as executable with chmod +x dealwithit, and then run it just by typing its filename.
But you'd like to know more. This script you've written is not very robust; if you run it in the wrong directory, you get a cascade of ugly error messages. And the script is not very flexible either—if you'd like to print, mail, backup, and delete some other file, you'll have to create another version of the script. Finally, if someone asks you what kind of shell script you've written—Bourne? Korn? C shell?—you can't say. Then read on.
Last question first: what kind of shell script is this? Actually, the script above is quite generic. It uses only features common to all the shells. Lucky you. As your shell scripts get more complex, you'll need to put a directive at the beginning to tell the operating system what sort of shell script this is.
#!/bin/bash
The #! should be the first two characters of the file, and the rest should be the complete pathname of the shell program you intend this script to be run by. Astute readers will note that this line looks like a comment, and, since it begins with a # character, it is one, syntactically. It's also magic.
When the operating system tries to run a file as a program, it reads the first few bytes of the file (its “magic number”) to learn what kind of file it is. The byte pattern #! means that this is a shell script, and that the next several bytes, up to a newline character, make up the name of the binary that the OS should really run, feeding it this script file.
Paranoid programmers will make sure that no spaces are placed after the executable name on the #! line. You are paranoid, aren't you? Good. Also, notice that running a shell script requires that it be read first; this is why you must have both read and execute permission to run a shell script file, while you need only execute permission to run a binary file.
In this article, we will focus on writing programs for the Bourne shell and its descendants. A Bourne shell script will run flawlessly not only under the Bourne shell, but also under the Korn shell, which adds a variety of features for efficiency and ease of use. The Korn shell itself has two descendents of its own: the POSIX standard shell, which is virtually identical to the Korn shell; and a big Linux favorite, the Bourne Again shell. The Bourne Again shell (“bash”) adds mostly interactive features from the descendants of the C shell, Bill Joy's attempt to introduce a shell that would use C-like control structures. What a great idea! Due to a few good reasons, most shell programming has followed the Bourne shell side of the genealogical tree. But people love the C shell's interactive features, which is why they too were incorporated into the Bourne Again shell.
Let me rephrase: do not write C shell scripts. Continue to use the C shell, or its descendant tcsh, interactively if you care to; your author does. But learn and use the Bourne/Korn/bash shells for scripting.
So here is our shell script now:
#!/bin/bash
lpr weekly.report
Mail boss < weekly.report
cp weekly.report /floppy/report.txt
rm weekly.report
If we run this script in the wrong directory, or if we accidentally name our file something other than “weekly.report”, here's what happens:
lpr: weekly.report: No such file or directory
./dealwithit: weekly.report: No such file or directory
cp: weekly.report: No such file or directory
rm: weekly.report: No such file or directory
and we get a bunch of “Permission denied” messages if we run the script when the permissions on the file are wrong. Bleah. Couldn't we do a check at the beginning of the program, so that if something is wrong we can avert all these ugly messages? Indeed we can, using (surprise!) an if.
It occurs to us that if cat weekly.report works, so will most of the things our script wants to do. The shell's if statement works just as this thought suggests: you give the if statement a command to try, and if the command runs successfully, it will run the other commands for you too. You also can specify some commands to run if the first command—called the “control command”--fails. Let's give it a try:
#!/bin/bash
if
cat weekly.report
then
lpr weekly.report
Mail boss < weekly.report
cp weekly.report /floppy/report.txt
rm weekly.report
else
echo I see no weekly.report file here.
fi
The indentation is not mandatory, but does make your shell scripts easier to read. You can put the control command on the same line as the if keyword itself.
This new version works great when there's an error. We get only one “No such file or directory” message, an improvement over four, and then our helpful personalized error message appears. But the script isn't so hot when it works: now we get the contents of weekly.report dumped to the screen as a preliminary. This is, after all, what cat does. Couldn't it just shut up?
You may know something about redirecting input and output in the world of Unix: the > character sends a command's output to a file, and the < character arranges for a command to get its input from a file, as in our Mail command. So if only we could send the cat command's output to the trash can instead of to a file... Wait! Maybe there's a trash can file somewhere. There is: /dev/null. Any output sent to /dev/null dribbles out the back of the computer. So let's change our cat command to:
cat weekly.report >/dev/null
Because you are paranoid, you may be wondering whether sending output to the trash can will affect whether this command succeeds or fails. Since /dev/null always exists and is writable by anyone, it will not fail.
Now our script is much quieter. But when cat fails, we still see the
cat: weekly.report: No such file or directory
error message. Why didn't this go into the trash can too? Because error messages flow separately from output, even though they usually share a common destination: the screen. We redirected standard output, but said nothing about errors. To redirect the errors, we can:
cat weekly.report >/dev/null 2>/dev/null
Just as > means “Send output here,” 2> means “Send errors here.” In fact, > is really just a synonym for 1>. Another, terser way to say the above command is this:
cat weekly.report >/dev/null 2>&1
The incantation 2>&1 means “Send errors (output stream number 2) to the same place ordinary output (output stream number 1) is going to.” By the way, this 2> jazz only works in the Bourne shell and its descendants. The C shell makes it annoying to separate errors from output, which is one of the reasons people avoid programming in it.
You may be saying to yourself: “This cat trick is fun, but isn't there some way I can just give a true-or-false expression? Like, either the file exists and is readable, or not?” Yes, you can. There is a command whose whole job is to succeed or fail depending on whether the expression you give is true or not: test. This is why your test programs called “test” never work, by the way. Here is our program, rewritten to use test:
#!/bin/bash
if
test -r weekly.report
then
lpr weekly.report
Mail boss < weekly.report
cp weekly.report /floppy/report.txt
rm weekly.report
else
echo I see no weekly.report file here.
fi
The test command's -r operator means, “Does this file exist, and can I read it?” test is quiet regardless of whether it succeeds or fails, so there's no need for anything to get sent to /dev/null.
Test also has an alternative syntax: you can use a [ character instead of the word test, so long as you have a ] at the end of the line. Be sure to put a space between any other characters and the [ and the ] characters! We can make our if look like this now:
if [ -r weekly.report ]
Hey, now that looks like a program! Even though we're using brackets, this is still the test command. There are lots of other things test can do for you; see its man page for the complete list. For example, we seem to recall that what lets you delete a file is not whether you can read it, but whether the directory it sits in gives you write permission. So we can re-write our script like this:
#!/bin/bash
if [ ! -r weekly.report ]
then
echo I see no weekly.report file here.
exit 1
fi
if [ ! -w . ]
then
echo I will not be able to delete
echo weekly.report for you, so I give up.
exit 2
fi
# Real work omitted...
Each test now has a ! character in it, which means “not”. So the first test succeeds if the weekly.report is not readable, and the second succeeds if the current directory (“.”) is not writable. In each case, the script prints an error message and exits. Notice that there's a different number fed to exit each time. This is how Unix commands (including if itself!) tell whether other commands succeed: if they exit with any exit code other than 0, they didn't. What each non-zero number (up to 255) means, other than “Something bad happened,” is up to you. But 0 always means success.
If this seems backwards to you, give yourself a cookie. It is backwards. But there's a good design reason for it, and it's a universal Unix-command convention, so get used to it.
Notice also that our real work no longer has an if wrapped around it. Our script will only get that far if none of our error conditions are detected. So we can just assume that all those error conditions are not in fact present! Real shell scripts exploit this property ruthlessly, often beginning with screenfuls of tests before any real work is done.
Now that we've made our script more robust, let's work on making it more general. Most Unix commands can take an argument from their command lines that tells them what to do; why can't our script? Because it has “weekly.report” littered all through it, that's why. We need to replace weekly.report with something that means “the thing on the command line.” Meet $1.
#!/bin/bash
if [ ! -r $1 ]
then
echo I see no $1 file here.
exit 1
fi
if [ ! -w . ]
then
echo I will not be able to delete $1 for you.
echo So I give up.
exit 2
fi
lpr $1
Mail boss < $1
# and so forth...
exit 0
$1 means the first argument on the command line. Yes, $2 means the second, $3 means the third, and so on. What's $0? The name of the command itself. So we can change our error messages so that they look like this:
echo $0: I see no $1 file here.
Ever noticed that Unix error messages introduce themselves? That's how.
Unfortunately, now there's a new threat to our program: what if the user forgets to put an argument on the command line? Then the right thing for $1 to have in it would be nothing at all. We might be back to our cascade of error messages, since a lot of commands, such as rm, complain at you if you put nothing at all on their command lines. In this program's case, it's even worse, since the first time $1 is used is as an argument to test -r, and test will give you a syntax error if you ask it to test -r nothing at all. And what does lpr do if you put nothing at all on its command line? Try it! But be prepared; you could end up with a mess.
Fortunately, test can help. Let's put this as the very first test in our program, right after the #!/bin/bash:
if [ -z "$1" ]
then
echo $0: usage: $0 filename
exit 3
fi
Now if the user puts nothing on the command line, we print a usage message and quit. The -z operator means “is this an empty string?”. Notice the double quotes around the $1: they are mandatory here. If you leave them out, test will give an error message in just the situation we are trying to detect. The quotes protect the nothing-at-all stored in $1 from causing a syntax error.
This if clause appears at the very top of many, many shell scripts. Among its other benefits, it relieves us from having to wrap $1 in quote marks later in our program, since if $1 were empty we would have exited at the start. In fact, the only time quotes would still be necessary would be if $1 could contain characters with a special meaning to the shell, such as a space or a question mark. Filenames don't, usually.
What if we want our script to be able to take a variable number of arguments? Most Unix commands can, after all. One way is clear: we could just cut and paste all the stuff in our shell script, so we'd have a bunch of commands that dealt with $1, then a bunch of commands that dealt with $2, and so forth. Sound like a good idea? No? Good for you; it's a terrible idea.
First of all, there would be some fixed upper limit on the number of arguments we could handle, determined by when we got tired of cutting, pasting, and editing our script. Second, any time you have many copies of the same code, you have a quality problem waiting to happen. You'll forget to make a change, or fix a bug, all of the many places necessary. Third, we often hand wildcards, like *, to Unix commands on their command lines. These wildcards are expanded into a list of filenames before the command runs! So it's very easy to get a command line with more arguments than some arbitrary, low limit.
Maybe we could use some kind of arithmetic trick to count through our arguments, like $i or something. This won't work either. The expression $i means “the contents of the variable called i”, not “the i'th thing on the command line.” Furthermore, not all shells let you refer to command-line words after $9 at all, and those that do make you use ${10}, ${11}, and so forth.
So what do we do? This:
while [ ! -z "$1" ]
do
# do stuff to $1
shift
done
Here's how we read that script: “While there's something in $1, we mess with it. Immediately after we finish messing with it, we do the shift command, which moves the contents of $2 into $1, the contents of $3 into $2, and so forth, regardless of how many of these command-line arguments there are. Then we go back and do it all again. We know we've finished when there's nothing at all in $1.”
This technique allows us to write a script that can handle any number of arguments, while only dealing with $1 at a time. So now our script looks like this:
#!/bin/bash
while [ ! -z "$1" ]
do
# do stuff to $1
if [ ! -r $1 ]
then
echo $0: I see no $1 file here.
exit 1
fi
# omitted test...
lpr $1
Mail boss < $1
# and so forth...
shift
done
exit 0
Notice that we nested if inside while. We can do that all we like. Also notice that this program quits the instant it finds something wrong. If you would like it to continue on to the next argument instead of bombing out, just replace an exit with:
shift
continue
The continue command just means “Go back up to the top of the loop right now, and try the control command again.” Thought question: why did we have to put a shift right before the continue?
Here's a potential problem: we've made it easy for someone to use this program on files that live in different directories. But we're only testing the current directory for writability. Instead, we should do this:
if [ ! -w `dirname $1` ]
then
echo $0: I will not be able to delete $1 for you.
# ...
The dirname command prints out what directory a file is in, judging from its pathname. If you give dirname a filename that doesn't start with a directory, it will print “.”--the current directory. And those backquotes? Unlike all other kinds of quotation marks, they don't mean “this is really all one piece ignore spaces.” Instead, backquotes—also called “grave accents”--mean “Run the command inside the backquotes before you run the whole command line. Capture all of the backquoted command's output, and pretend that was what appeared on the larger command line instead of the junk in backquotes.” In other words, we are substituting a command's output into another command line.
So here is the final version of our shell script:
#!/bin/bash
while [ ! -z "$1" ]
do
if [ ! -r $1 ]
then
echo $0: I see no $1 file here.
shift
continue
fi
if [ ! -w `dirname $1` ]
then
echo $0: I will not be able to delete $1 for you.
shift
continue
fi
lpr $1
Mail boss < $1
cp $1 /floppy/`basename $1`
rm $1
shift
done
exit 0
An exercise for the reader: what does `basename $1` do?
Now there are only two other techniques you need to know to meet the vast majority of your scripting needs. First, suppose you really do need to count. How do we do the equivalent of a C for loop? Here's the traditional Bourne shell way:
i=0
upperlim=10
while [ $i -lt $upperlim ]
do
# mess with $i
i=`expr $i + 1`
done
Notice that we did not use the for keyword. for is for something else entirely. Instead, here we initialize a variable i to 0, then we enter and remain in the loop as long as the value in i is less than 10. (Fortran programmers will recognize -lt as the less-than operator; guess why > is not used in this context.) The rather mysterious line
i=`expr $i + 1`
calls the expr command, which evaluates arithmetic expression. We stuff expr's output back into i using backquotes.
Ugly, isn't it? And not especially fast either, since we are running a command every time we want to add 1 to i. Can't the shell just do the arithmetic itself? If the shell is the Bourne shell, no, it can't. But the Korn shell can:
((i=i+1))
Use that syntax if it works, and if you don't need portability. The bash shell uses something similar:
i=$(($i+1))
which is a bit more portable (it even works in the Korn shell), since it is specified by POSIX, but still won't work for some non-POSIX bourne shells.
So what does for do? It allows you to wade through a list of items, assigning a variable to each element of the list in turn. Here's a trivial example:
for a in Larry Moe Curly
do
echo $a
done
which would print
Larry
Moe
Curly
Less trivially, we can use this to handle the case where we want to do something for each word in a variable:
mylist="apple banana cheese rutabaga"
for w in $mylist
do
# mess with $w
done
or for each file matched by a shell wildcard pattern:
for f in /docs/reports/*.txt
do
pr -h $f $f | lpr
done
or for each word in the output of a command:
for a in `cat people.txt`
do
banner $a
done
Here's how you can use for to simulate the C for you know and love:
for i in 0 1 2 3 4 5 6 7 8 9 10 11
do
# mess with $i
done
Of course, it'd be very difficult to have a variable upper limit with this syntax, which is why we usually use the while loop shown above.
Congratulations! You've now seen what's at work in the vast bulk of practical shell scripts. Go forth and save time!
Brian Rice (rice@kcomputing.com) s Member of Technical Staff with K Computing, a nationwide Unix and Internet training firm.
Validating an IP Address in a Bash Script
June 26th, 2008 by Mitch Frazier in
I've recently written about using bash arrays and bash regular expressions, so here's a more useful example of using them to test IP addresses for validity.
To belabor the obvious: IP addresses are 32 bit values written as four numbers (the individual bytes of the IP address) separated by dots (periods). Each of the four numbers has a valid range of 0 to 255.
The following bash script contains a bash function which returns true if it is passed a valid IP address and false otherwise. In bash speak true means it exits with a zero status, anything else is false. The status of a command/function is stored in the bash variable "$?".
#!/bin/bash
# Test an IP address for validity:
# Usage:
# valid_ip IP_ADDRESS
# if [[ $? -eq 0 ]]; then echo good; else echo bad; fi
# OR
# if valid_ip IP_ADDRESS; then echo good; else echo bad; fi
#
function valid_ip()
{
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return $stat
}
# If run directly, execute some tests.
if [[ "$(basename $0 .sh)" == 'valid_ip' ]]; then
ips='
4.2.2.2
a.b.c.d
192.168.1.1
0.0.0.0
255.255.255.255
255.255.255.256
192.168.0.1
192.168.0
1234.123.123.123
'
for ip in $ips
do
if valid_ip $ip; then stat='good'; else stat='bad'; fi
printf "%-20s: %s\n" "$ip" "$stat"
done
fi
If you save this script as "valid_ip.sh" and then run it directly it will run some tests and prints the results:
# sh valid_ip.sh
4.2.2.2 : good
a.b.c.d : bad
192.168.1.1 : good
0.0.0.0 : good
255.255.255.255 : good
255.255.255.256 : bad
192.168.0.1 : good
192.168.0 : bad
1234.123.123.123 : bad
In the function valid_ip, the if statement uses a regular expression to make sure the subject IP address consists of four dot separated numbers:
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
If that test passes then the code inside the if statement separates the subject IP address into four parts at the dots and places the parts in an array:
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
It does this by momentarily changing bash's Internal Field Separator variable so that rather than parsing words as whitespace separated items, bash parses them as dot separated. Putting the value of the subject IP address inside parenthesis and assigning it to itself thereby turns it into an array where each dot separated number is assigned to an array slot. Now the individual pieces are tested to make sure they're all less than or equal to 255 and the status of the test is saved so that it can be returned to the caller:
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
Note that there's no need to test that the numbers are greater than or equal to zero because the regular expression test has already eliminated any thing that doesn't consist of only dots and digits. __________________________
Mitch Frazier is the System Administrator at Linux Journal.
Tuesday, June 17, 2008
Things to do after installing openSUSE 11.0
Ben Kevan’s Blog :
So you’ve downloaded and installed openSUSE 11.0. Are you now wonderingwhat you may have to do post installation, here’s a quick run down:
Setup suggested repositoriesSetup your Video Card (if you have more configurations to do, ie Nvidia / ATI)
Get Compiz running (if you wish)
Codec Support (Restricted Formats, multimedia)
Install various applications
Setting up repositories:
For my machine I added the following repositories for my final build:http://download.videolan.org/pub/vlc/SuSE/11.0 (VLC Media, restricted formats)
http://download.opensuse.org/repositories/KDE:/KDE4:/STABLE:/Desktop/openSUSE_11.0/
(KDE 4 Stable) Note: if this was not my workbox, I would actually do
Unstable to get KDE 4.1
http://download.opensuse.org/repositories/Emulators:/Wine/openSUSE_11.0/ (Wine, for windows emulation)
http://download.opensuse.org/repositories/KDE:/Backports/openSUSE_11.0/
(KDE Backports, updates to Amarok and other KDE Applications)
http://download.opensuse.org/repositories/X11:/XGL/openSUSE_11.0/
(Compiz Fusion, staying up to date and hip with the most recent Compiz
builds, I highly recommend since it’s constantly developed on)
http://ftp.skynet.be/pub/packman/suse/11.0/ (Packman, xinelibs / codecs and so much more)
http://download.opensuse.org/repositories/mozilla/openSUSE_11.0/ (Firefox, recent builds of Firefox)
http://download.opensuse.org/repositories/GNOME:/Community/openSUSE_11.0/ (Gnome Community, I use for Pidgin and GIMP)
http://download.opensuse.org/repositories/OpenOffice.org:/STABLE/openSUSE_11.0/
(OpenOffice is an important application for me since I use it daily for
word processing or spreadsheets, keeping it up to date allows for the
best compatabilities with people using restricted formats like xls/doc)
http://download.opensuse.org/repositories/Education:/desktop/openSUSE_11.0/ (Education - For Bluefish)
http://download.opensuse.org/repositories/KDE:/KDE3/openSUSE_11.0/ (KDE
Stable repository, not sure why because not many changes should happen
to the 3.5.9 family)
Here is a quick screenshot of my repositories:
(Note: I am running KDE, if you are running GNOME, I highly recommend the following repo:
http://download.opensuse.org/repositories/GNOME:/STABLE/openSUSE_11.0/)
(Note: If you are running an NVIDIA card, setup the following repository:
ftp://download.nvidia.com/opensuse/11.0/)
(Note: If you are running an ATI card, setup the following repository:
http://www2.ati.com/suse/11.0/)
(Note: If you are running a wireless card that has a requirement for MadWifi, setup the following repository:
http://madwifi.org/suse/11.0/)
Setup your Video Card (if you have more configurations to do, ie Nvidia / ATI)
Install the packages that are associated with your NVIDIA / ATI card.
For me I downloaded the NVIDIA Driver from nvidia.com and installing running:
sudo sh NVIDIA-Linux-x86-173.08-pkg1.run
Since I had some issues with the 173.14 driver.
Get Compiz running (if you wish)
With openSUSE 11.0 it works out of the box, but I recommend installing
some of the other packages that don’t come installed by default also
updating to the Compiz Fusions newest build service.
Here are the compiz packages I have installed:
rpm -qa | grep compiz
compiz-fusion-plugins-main-0.7.6-2.6
compiz-fusion-plugins-extra-0.7.6-1.3
compizconfig-settings-manager-0.7.6-1.6
compiz-kde-0.7.6-2.5
compiz-emerald-0.7.6-1.6
compiz-manager-0.0.1_git080201-24.1
compiz-emerald-themes-0.6.0-16.1
compiz-0.7.6-2.5
libcompizconfig-0.7.6-1.6
python-compizconfig-0.7.6-1.6
sudo zypper up -t package -r “Compiz Fusion”
(Note: I named my Compiz Fusion repository Compiz Fusion, the -r switch
is for your Repo Alias that you have included into your Repository
Configuraiton)
Codec Support (Restricted Formats, multimedia)
Install the restricted formats you want to (i personally do not install
libdvdcss since I have no need to play dvd’s on my work machine, and it
is prohibited in the US (for now)). This should allow you to play avi /
mpg / mp3 / dvd’s and many other formats
You can install by running: (this requires packman and vlc repositories)
sudo zypper in libdvdcss libxine1 w32codec-all k3b-codecs
Note: if this is a home machine for multimedia I recommend also installing vlc by running:
sudo zypper in vlc
Install various applications
I typically install wine, opera, pidgin and bluefish from the repos.
I also install vmware for my daily work needs.
You can install running:
sudo zypper in wine opera pidgin bluefish
sudo rpm -ivh sudo rpm -ivh VMware-workstation-6.0.4-93057.i386.rpm
I then configured vmware by running:
sudo /usr/bin/vmware-config.pl
(Note: you have to install kernel-source, gcc, gcc++ and make)
You can install running:
sudo zypper in make kernel-source gcc gcc-c++
You can check my wiki located here for more indept installation procedures
Here is a screenshot of my machine running KDE 3.5.9, openSUSE 11.0 and Compiz Fusion:
Bonus: I also installed the Oxygen packages for yast, to beautify my KDE 3.5.9 yast, you can do the same by running:
sudo zypper in yast2-theme-openSUSE-Oxygen
Please if you believe there is more to be done after installing
openSUSE 11.0 let me know so I can update this, and it can be helpful
to a bigger vast of people.
Programming Python, Part II
July 1st, 2007 by José P. E. "Pupeno" Fernandez in
* HOWTOs
Having covered some advanced features in Part I, it's time to include some basics.
The tutorial in last month's issue covered the basics of installing Python, running it and using it. Then, we moved on to building a basic blog in Python. The blog was extremely simple—only a Python list. We focused on the posts and built a Post class:
class Post(object):
def __init__(self, title, body):
self.set_title(title)
self.set_body(body)
def set_title(self, title):
self._title = title
def get_title(self):
return self._title
def set_body(self, body):
self._body = body
def get_body(self):
return self._body
def __repr__(self):
return "Blog Post: %s" % self.get_title()
In this follow-up article, let's focus on the blog itself and go further.
The Blog
Now that we have the Post class, we can make the Blog class. An initial implementation may look like this:
class Blog(object):
def __init__(self):
self._posts = []
def add_post(self, post):
self._posts.append(post)
def get_posts(self):
return self._posts
We are using a list to maintain the posts, but the interface is totally abstract behind a set of methods in the Blog class. This has a huge advantage: tomorrow we could replace that simple list with an SQL back end, and the code that uses Blog will need few, if any, changes.
Notice that there's no way to delete a post. We could tamper with _posts directly, but as long as we do what the class was meant to do, we can't delete a post. That may be good or bad, but the important thing is that by defining a set of methods, we exposed the design of how the class should be used.
To Publish or Not to Publish
The method get_posts returns all the posts. When we are writing a new post, we don't want the whole world to be able to read it until it is finished. The posts need a new member that tell whether it is published. In Post's initalizator, __init__, we add the line:
self._published = False
That makes every new post private by default. To switch states, we add the methods:
def publish(self):
self._published = True
def hide(self):
self._published = False
def is_public(self):
return self._published
In these methods, I introduced a new kind of variable—the boolean. Booleans are simple; they can be true or false. Let's play with that a bit:
/>>> cool = blog.Post("Cool", "Python is cool")
/>>> cool.is_public()
False
/>>> cool.publish()
/>>> cool.is_public()
True
/>>> cool.hide()
/>>> cool.is_public()
False
/>>>
If, when you run is_public, you get:
Traceback (most recent call last):
File "", line 1, in ?
File "blog.py", line 25, in is_public
return self._published
AttributeError: 'Post' object has no attribute
'_published'
That's because _published was not created, it can't be used, and is_public wants to use it. Understanding errors in your tools is important if you want to be a successful programmer.
In this short set of messages, the last line is the error itself. There are various types of errors, and this one is an AttributeError. A lot of important information is given in the traceback. A traceback is a list of “who called whom”, providing an idea of what was being executed when the error occurred.
The first line of the traceback doesn't give much information. It probably relates to the line we typed at the REPL. The second line tells that the error was in the file blog.py, on line 25, on the method is_public. Now we have the line that raised the problem.
This traceback is simple. In a real application, you would have methods that call methods that call methods and so on. In those cases, it is not uncommon to see tracebacks of 25 lines or more. I've seen tracebacks of more than 150 lines, but those were extreme cases in extreme conditions.
The next step is a modification to the Blog class to pick up only published posts. So, we add a new method:
def get_public_posts(self):
published_posts = []
for post in self._posts:
if port.is_public():
published_posts.append(post)
Python tries to be as readable as possible, but that method introduces too many new things, so it requires some careful explanations.
Loops
One of the Python's looping constructs is for. It is designed to iterate over lists, sets, maps and other iterable objects. In this case, it takes all the items in self._posts and, one by one, assigns them to the variable post. In the body of the for, which is executed on each iteration, we can use the variable post.
The body of the for, as with other constructs that need a piece of code, is delimited by nothing more than the indentation. Here's an example:
/>>> the_list = [1,2,3,"a","b"]
/>>> for item in the_list:
... print item
...
1
2
3
a
b
/>>>
Various tasks are solved with a loop. One such task is doing something for each member of a collection, like we did in the previous example. For those types of tasks, the for construct is excellent.
Another common practice is to perform an action a given number of times—for example, printing “Hello, world” three times. To do that we can use:
/>>> a = 0
/>>> while a < 3:
... print "Hello world"
... a = a + 1
...
Hello world
Hello world
Hello world
/>>>
Another loop construct is while, and it will continue to run its body until the check—that is, the expression after while and before the colon—becomes false.
We can rethink the previous loop as iterating over a list containing the numbers 0–9. There's a way to do it with a for construct:
/>>> for a in range(0,3):
... print "Hello world"
...
Hello world
Hello world
Hello world
/>>>>
This is shorter and arguably more readable. What is while useful for then? It is useful any time you don't really know when you are going to stop the loop. Here are some examples:
*
Reading characters from a file until you encounter the End of File (EOF).
*
Reading commands from a user until the user enters the quit command.
*
Reading temperatures from a sensor until the temperature is too high.
*
Reading events from a user interface until the user presses the X button at the top of the window to close the program.
There's a pattern forming here—doing something until something else happens. That's what while is good for.
Some time ago, when we didn't have as many choices in programming languages and we ended up using C most of the time, the while construct tended to be much more useful than the for construct. But today, with a powerful for construct, nice functions such as range and the possibility of putting an iterator around anything, for is being used much more than while.
Here's one last example for your enjoyment:
/>>> for l in "Hello World":
... print l + " ",
...
H e l l o W o r l d
Conditionals
In the fourth line of some previous sample code, if post.is_public(), we have another new construct—an if. This allows programs to make choices based on data. It needs a boolean value and a piece of code. The code is run only if the boolean is True. If you provide something that is not a boolean, Python does its best to interpret it as a boolean. For example, the number 0 is interpreted as False, but all the other numbers as True. Here are some examples:
/>>> if True:
... print "It is true!"
...
It is true!
/>>> if False:
... print "Is it false?"
...
/>>>
We can perform many different types of comparisons on different kinds of objects. Note that the equality operator is ==, not = (that is, two equal signs):
/>>> a = 10
/>>> if a == 10:
... print "Ten!"
...
Ten!
There are other comparisons, such as greater than (>), less than (<) and different (!=). You can experiment with comparisons directly on the REPL:
/>>> 3 == 4
False
/>>> 10 != 5
True
/>>> 4 >= 1
True
It is common to run a piece of code if something is true and another piece of code if it is false. For example, we could do the following:
if a == 10:
print "A is ten."
if a != 10:
print "A is not ten."
This has a big problem. If we change a to b in the first case, we have to remember to change it in the second. And, the same should be done for any other little changes we do. The solution is an extension to the if construct:
if a == 10:
print "A is ten."
else:
print "A is not ten."
The piece of code after the else will be executed if the first piece wasn't executed.
Another common situation is having various conditionals for different cases. In that case, we use a string of ifs:
if a == 10:
print "A is ten."
elif a == 0:
print "A is zero."
elif a != 30:
print "A is not thirty."
else:
print "Who cares about a ?"
elif is the contraction of “else if”, and indeed, the previous code could be written as:
if a == 10:
print "A is ten."
else:
if a == 0:
print "A is zero."
else:
if a != 30:
print "A is not thirty."
else:
print "Who cares about a ?"
But, that is ugly and prone to errors. If you have 10 or 15 different cases, you'll need a 29"-widescreen monitor just to view it. (Not that I have anything against such a monitor. I'd like to have one.)
If you come from other languages that have a switch or select or case construct and are wondering where they are in Python, I'm sorry to disappoint you. Python doesn't have such constructs. There's a proposal to include them, but it hasn't been implemented yet. Right now, the solution is to use a chain of ifs, elifs and elses. After you use this a few times, it's not so bad.
Now that you know about else, here's an interesting tidbit: for and while also can have elses. What do they do? Run Python, and try it out until you discover for yourself. While programming, you'll need to run a lot of code to find out how many undocumented, obscure, almost black-magic, things work, so starting with something simple will help you get some training.
Inheritance
The short introduction to object-oriented programming (OOP) in Part I of this article left out a big topic—inheritance. This feature is what makes OOP really useful, and as OOP tries to mimic real life, I explain inheritance here with real-life examples.
Think about a chair. A chair is made out of some kind of material, has two armrests, a back, a color, a style and maybe even a warranty. Now, think about a table. It is made out of some kind of material, might have some drawers, a color, a style and maybe a warranty. They have a lot in common! If we were to make the two classes, Chair and Table, a lot of code would be repeated. In programming, when you write the same line of code twice, you probably are doing something wrong—inheritance to the rescue.
A chair is a piece of furniture. So is a table. Such similarities can be in the Furniture class. Let's make the Furniture class have a default material and the ability to set other materials:
class Furniture(object):
def __init__(self):
self._material = "wood"
def set_material(self, material):
self._material = material
And now, a Chair class inheriting Furniture:
class Chair(Furniture):
def __init__(self):
self._backrest_height = 30
def set_backrest_height(self, height):
self._backrest_height = height
Now, you know what goes inside parentheses in the class header: the name of the class being inherited, which also is known as a super class or parent class. Let's play a bit with this, so you can see what happens:
/>>> c = Chair()
/>>> c.set_backrest_height(50)
/>>> c._backrest_height
50
/>>> c.set_material("plastic")
/>>> c._material
'plastic'
/>>>
As you can see, the methods of Furniture also are on Chair. I leave the definition of the Table class as an exercise for the reader. But first, here's another interaction:
/>>> d = Chair()
/>>> d._backrest_height
30
/>>> d._material
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'Chair' object has no attribute '_material'
/>>>
I bet that is not what you expected. Let's take a closer look at what happened. We created a Chair, the method Chair.__init__ was run setting _backrest_height. Oh! Nobody called Furniture.__init__, which never set _material. There are two solutions to that.
Setting _material in Chair.__init__ is not a solution. If we do that, the classes would be coupled, meaning the implementation of one will depend on the implementation of the other. If we change the name of _material to _materials, suddenly Chair will stop working. If you have hundreds of classes developed by hundreds of different people, keeping track of those changes is difficult. Also, Furniture will grow to have more members, so we have to remember to set all those members to the same defaults in Chair.__init__. I'm getting a headache just thinking about it.
One real solution is calling Furniture.__init__ and rewriting Chair.__init__ this way:
def __init__(self):
Furniture.__init__(self)
self._backrest_height = 30
We had to pass self to __init__, because if we called it with the class instead of the object, it wouldn't know in which object to do its operations.
I personally don't like that solution, because it implies writing the name of the class in two or more places. If you ever change the name, you'll have to remember to run a search and replace. Another solution is more cryptic than it should be, but it doesn't have the problem I just mentioned:
def __init__(self):
super(Chair, self).__init__()
self._backrest_height = 30
In this solution, I call super, passing the current class and the current object, and it allows me to make a call to the parent class using the current object. Here we may have a problem if we change the name of the class itself, but running a search and replace on the file is a good idea when making that kind of change. You'd want to change the documentation as well. The real problem with this solution is hard to understand and to explain—it has to do with multiple inheritance. For more information, read “Python's Super Considered Harmful”. Personally, I've been using this second solution without any problems.
You'll see that all classes I defined inherit from object. That is the most basic class—the root (or top) class. It is a good idea to make all your classes inherit from it unless they inherit from another class. If you don't do that, your class will be an old-style class, and some things won't work, such as super. It is important to know this, because you may encounter old-style classes anywhere, and you should be prepared.
Python 2.5
During the process of writing this article, with much excitement and fanfare, Python 2.5 was released. It is the most important release in almost two years, and it comes with many promises.
It promises to be more reliable due to improvements in the testing procedures used by the Python development team. It now has Buildbot, a program that continuously builds and tests Python, and whenever there's something wrong, it raises an alarm for all the world to see. The shame of being the developer who made the error will make all the developers more careful—at least, that's what happened to me when I had a Buildbot watching my code.
For some, like this author who had a new release at the worst possible time, the most important thing is that Python 2.5 is backward-compatible. All that you've learned here will work. And, not only will it work, it is still the way to do it.
The new release also promises to be faster and has many new advanced features, including new modules and packages. The future is bright for Python and Python coders.
What Now?
This was nothing but a short introduction to Python; there's still much to learn. A good place to start is the official Python Tutorial. You also can read Dive Into Python, a book that you can buy or read for free on the Web. And, of course, a lot of other books and tutorials are available. I learned Python mainly from the Python Tutorial, which is very good.
Whenever you are creating a program in Python, never, and I repeat, never, do anything without checking whether it has been done before. Python has a lot of features and a lot of built-in libraries. And, if that isn't enough, there are hundreds, maybe thousands of third-party Python libraries. In fact, the huge amount of code that's already written in Python is one of the reasons to use it.
The first stop is Python's Documentation. There we have the previously mentioned tutorial, the library reference and the language reference.
The language reference can be a bit hard to use and understand. Programming languages tend to be difficult to understand and so are their references, which often have exclusive jargon, such as lexical analysis, tokens, identifiers, keywords or delimiters. This piece of documentation can be particularly useful in showing how to use language constructs, such as for, if, while and more complex ones that I haven't mentioned, such as yield, break or continue.
The library references let us know about all the classes, methods and functions that Python already provides. It is so important and useful that I always have it open when I am programming on Python. In the second chapter, you can read about the built-in functions and classes. Getting familiar with them is always useful. The rest of the documentation is very specific, and each chapter deals with subjects ranging from runtime internals to string, from the Python debugger to some generic operating systems services. In that chapter, a very important module is documented: os. I can't remember making a single program that didn't use that module.
Finding what you want in so much documentation can be a difficult task. A trick that I find very useful is to use Google to search in a specific site. That is achieved by adding “site:python.org” or “site:docs.python.org” to the search query. The first one is more generic and sometimes leads to countless mailing-list posts that have nothing to do with what you are looking for. In that situation, use the second one. To give it a try, search for “print site:python.org” or “options site:python.org”.
What if all of your searches return nothing? Then, you need to do a broader search to find some third-party libraries or frameworks. If you want to make a graphical user interface, I recommend both PyGTK and PyQt, both are very good and include support for their respective desktops, GNOME and KDE. I've heard good opinions of wxPython, but I've not used it myself.
If you want to build a Web application, I see two paths. If you want something not so spectacular but that gets you there fast, I recommend Django. Django is very similar to Ruby on Rails. It's a framework in which you use the model-view-controller paradigm and a relational database such as MySQL or PostgreSQL; both are well supported on Python.
The other way to build Web sites (that I know of) is Zope. Zope is a big framework with a Web server and object-oriented database. The database is different from other relational databases, and it is very powerful. It allows you to store information in a much more flexible way. Zope 3—I don't recommend the previous versions unless you have to use the award-winning content management system Plone—is prepared to help you build reliable and robust code by means of interfaces, unit testing, adapters and much more.
If you need to build any kind of dæmon—those little applications running in the background making the earth turn—take a look at Twisted Matrix. Twisted Matrix is an event-based framework that solves a lot of the common problems of building dæmons, including separation of protocol and logic. It comes with many protocols already built in, and it allows you to create new protocols. A proof of its usefulness is that Zope, after years of shipping its own Web sever, has migrated to using the Twisted Matrix HTTP server.
Resources
Python Tutorial: docs.python.org/tut/tut.html
Dive Into Python: www.diveintopython.org
Python Documentation: www.python.org/doc
PyGTK: www.pygtk.org
PyQt: www.riverbankcomputing.co.uk/pyqt
Django: www.djangoproject.com
Zope: zope.org
Python's Super Considered Harmful: fuhm.net/super-harmful
José P. E. “Pupeno” Fernández has been programming since...at what age is a child capable of siting in a chair and reaching a keyboard? He has experimented with more languages than can be listed on this page. His Web site is at pupeno.com, and he always can be reached, unless you are a spammer, at pupeno@pupeno.com.
Programming Python, Part I
June 1st, 2007 by José P. E. "Pupeno" Fernandez in
* Software
This tutorial jumps right in to the power of Python without dragging you through basic programming.
Python is a programming language that is highly regarded for its simplicity and ease of use. It often is recommended to programming newcomers as a good starting point. Python also is a program that interprets programs written in Python. There are other implementations of Python, such as Jython (in Java), CLPython (Common Lisp), IronPython (.NET) and possibly more. Here, we use only Python.
Installing Python
Installing Python and getting it running is the first step. These days, it should be very easy. If you are running Gentoo GNU/Linux, you already have Python 2.4 installed. The packaging system for Gentoo, Portage, is written in Python. If you don't have it, your installation is broken.
If you are running Debian GNU/Linux, Ubuntu, Kubuntu or MEPIS, simply run the following (or log in as root and leave out sudo):
sudo apt-get install python
One catch is that Debian's stable Python is 2.3, while for the rest of the distributions, you are likely to find 2.4. They are not very different, and most code will run on both versions. The main differences I have encountered are in the API of some library classes, new features added to 2.4 and some internals, which shouldn't concern us here.
If you are running some other distribution, it is very likely that Python is prepackaged for it. Use the usual resources and tools you use for other packages to find the Python package.
If all that fails, you need to do a manual installation. It is not difficult, but be aware that it is easy to break your system unless you follow this simple guideline: install Python into a well-isolated place, I like /opt/python/2.4.3, or whatever version it is.
To perform the installation, download Python, unpack it, and run the following commands:
./configure --prefix=/opt/python2.4/
make
make install
This task is well documented on Python's README, which is included in the downloaded tarball; take a look at it for further details. The only missing task here is adding Python to your path. Alternatively, you can run it directly by calling it with its path, which I recommend for initial exploration.
First Steps
Now that we have Python running, let's jump right in to programming and examine the language as we go along. To start, let's build a blog engine. By engine, I mean that it won't have any kind of interface, such as a Web interface, but it's a good exercise anyway.
Python comes with an REPL—a nice invention courtesy of the Lisp community. REPL stands for Read Eval Print Loop, and it means there's a program that can read expressions and statements, evaluate them, print the result and wait for more. Let's run the REPL (adjust your path according to where you installed Python in the previous section):
$ python
Python 2.4.3 (#1, Sep 1 2006, 18:35:05)
[GCC 4.1.1 (Gentoo 4.1.1)] on linux2
Type "help", "copyright", "credits" or "license" for
more information.
/>>>
Those three greater-than signs (>>>) are the Python prompt where you write statements and expressions. To quit Python, press Ctrl-D.
Let's type some simple expressions:
/>>> 5
5
The value of 5 is, well, 5.
/>>> 10 + 4
14
That's more interesting, isn't it?
There are other kinds of expressions, such as a string:
/>>> "Hello"
'Hello'
Quotes are used to create strings. Single or double quotes are treated essentially the same. In fact, you can see that I used double quotes, and Python showed the strings in single quotes.
Another kind of expression is a list:
/>>> [1,3,2]
[1, 3, 2]
Square brackets are used to create lists in which items are separated by commas. And, as we can add numbers, we can add—actually concatenate—lists:
/>>> [1,3,2] + [11,3,2]
[1, 3, 2, 11, 3, 2]
By now, you might be getting bored. Let's switch to something more exciting—a blog. A blog is a sequence of posts, and a Python list is a good way to represent a blog, with posts as strings. In the REPL, we can build a simple blog like this:
/>>> ["My first post", "Python is cool"]
['My first post', 'Python is cool']
/>>>
That's a list of strings. You can make lists of whatever you want, including a list of lists. So far, all our expressions are evaluated, shown and lost. We have no way to recall our blog to add more items or to show them in a browser. Assignment comes to the rescue:
/>>> blog = ["My first post", "Python is cool"]
/>>>
Now blog, a so-called variable, contains the list. Unlike in the previous example, nothing was printed this time, because it is an assignment. Assignments are statements, and statements don't have a return value. Simply evaluating the variable shows us the content:
/>>> blog
['My first post', 'Python is cool']
Accessing our blog is easy. We simply identify each post by number:
/>>> blog[0]
'My first post'
/>>> blog[1]
'Python is cool'
Be aware that Python starts counting at 0.
Encapsulating Behavior
A blog is not a blog if we can't add new posts, so let's do that:
/>>> blog = blog + ["A new post."]
/>>> blog
['My first post', 'Python is cool', 'A new post.']
Here we set blog to a new value, which is the old blog, and a new post. Remembering all that merely to add a new post is not pleasant though, so we can encapsulate it in what is called a function:
/>>> def add_post(blog, new_post):
... return blog + [new_post]
...
/>>>
def is the keyword used to define a new function or method (more on functions in structured or functional programming and methods in object-oriented programming later in this article). What follows is the name of the function. Inside the parentheses, we have the formal parameters. Those are like variables that will be defined by the caller of the function. After the colon, the prompt has changed from >>> to ... to show that we are inside a definition. The function is composed of all those lines with a level of indentation below the level of the def line.
So, where other programming languages use curly braces or begin/end keywords, Python uses indentation. The idea is that if you are a good programmer, you'd indent it anyway, so we'll use that indentation and make you a good programmer at the same time. Indeed, it's a controversial issue; I didn't like it at first, but I learned to live with it.
While working with the REPL, you safely can press Tab to make an indentation level, and although a Tab character can do it, using four spaces is the strongly recommended way. Many text editors know to put four spaces when you press Tab when editing a Python file. Whatever you do, never, I repeat, never, mix Tabs with spaces. In other programming languages, it may make the community dislike you, but in Python, it'll make your program fail with weird error messages.
Being practical, to reproduce what I did, simply type the class header, def add_post(blog, new_post):, press Enter, press Tab, type return blog + [new_post], press Enter, press Enter again, and that's it. Let's see the function in action:
/>>> blog = add_post(blog, "Fourth post")
/>>> blog
['My first post', 'Python is cool', 'A new post.',
'Fourth post']
/>>>
add_post takes two parameters. The first is the blog itself, and it gets assigned to blog. This is tricky. The blog inside the function is not the same as the blog outside the function. They are in different scopes. That's why the following:
/>>> def add_post(blog, new_post):
... blog = blog + [new_post]
doesn't work. blog is modified only inside the function. By now, you might know that new_post contains the post passed to the function.
Our blog is growing, and it is time to see that the posts are simply strings, but we want to have a title and a body. One way to do this is to use tuples, like this:
/>>> blog = []
/>>> blog = add_post(blog, ("New blog", "First post"))
/>>> blog = add_post(blog, ("Cool", "Python is cool"))
/>>> blog
[('New blog', 'First post'),
('Cool', 'Python and is cool')]
/>>>
In the first line, I reset the blog to be an empty list. Then, I added two posts. See the double parentheses? The outside parentheses are part of the function call, and the inside parentheses are the creation of a tuple.
A tuple is created by parentheses, and its members are separated by commas. They are similar to lists, but semantically, they are different. For example, you can't update the members of a tuple. Tuples are used to build some kind of structure with a fixed set of elements. Let's see a tuple outside of our blog:
/>>> (1,2,3)
(1, 2, 3)
Accessing each part of the posts is similar to accessing each part of the blog:
/>>> blog[0][0]
'New blog'
/>>> blog[0][1]
'This is my first post'
This might be a good solution if we want to store only a title and a body. But, how long until we want to add the date and time, excerpts, tags or messages? You may begin thinking you'll need to hang a sheet of paper on the wall, as shown in Figure 1, to remember the index of each field—not pleasant at all. To solve this problem, and some others, Python gives us object-oriented programming.
Figure 1. Index Handling the Hard Way
Object-Oriented Programming
Object-oriented programming was born more than 20 years ago so developers could separate each part of a computer program in a way similar to how objects are separated in the real world. Python models objects by using classes. A class is an abstract definition of what an object has and what an object can do. If this sounds foreign, don't worry, OOP (object-oriented programming) is difficult at first.
An example might help. A bridge is a structure that allows people or vehicles to cross an obstacle, such as a river, canal or railway. A bridge has some length, some width and even some color. It may allow vehicles or only persons. It may allow heavy vehicles or not. When I say “bridge”, I am not defining any of those details. Bridge is a class. If I say Golden Gate, Le Pont de Normandie or Akashi-Kaikyo, I am naming particular bridges; they have some specific length, width, vehicle allowance and color. In OOP jargon, they are instances of bridge.
Back to our blog, let's create a class to model our post:
/>>> class Post(object):
... pass
...
/>>>
We start with class, the keyword for creating new classes. Next comes the name of the class—in this case, Post. In parentheses, we have the super-classes—ignore that for now.
Here again, the prompt has changed from >>> to ..., and Python expects something in a class. Because we don't want to put anything in yet, we write pass, which is something, but in fact, it is nothing. Python knows when a class starts and ends because of the indentation, the same as with functions.
To reproduce what I did, simply type the class header, class Post(object):, press Enter, press Tab, type pass, press Enter, press Enter again, and that's it.
Now, we can create a Post:
/>>> cool = Post()
/>>> cool
<__main__.Post object at 0xb7ca642c>
Note that what is being printed when we evaluate a post is a generic representation for the object. We can set its title and body:
/>>> cool.title = "Cool"
/>>> cool.body = "Python is cool."
And retrieve them:
/>>> cool.title
'Cool'
/>>> cool.body
'Python is cool.'
Up to this point, a Post is like a simple container for anything you can imagine putting there. The problem with this is we may get lost as to what to put in it, or what not to put in it. Back to a sheet of paper? No! Although we can't stop making the posts a container in that way, we can put some methods there, so users have an idea of what a post may contain. To do this, we write our own methods in the class Post:
/>>> class Post(object):
... def set_title(self, title):
... self._title = title
... def get_title(self):
... return self._title
...
/>>>
Methods are like functions, but as they are in a class, they are associated with the objects of the class. This means different classes can have different methods with the same name. Just imagine the difference between bat.hit(ball) and stick.hit(drum).
Python has a convention that the first parameter (normally called self) to a method is the object on which we are calling the method. That means running cool.set_title("Cool")will set self to be cool, and title to be "Cool". Running:
cool.set_title("Cool")
is the equivalent of:
cool._title = "Cool"
The leading underscore lets others know that we don't want them playing with it. It means “don't access _title; use get_title and set_title”.
The previous interaction with the cool object can be rewritten as:
/>>> cool = Post()
/>>> cool.set_title("Cool")
/>>> cool.set_body("Python is cool.")
/>>> cool.get_title()
'Cool'
/>>> cool.get_body()
'Python is cool.'
Writing the same set of methods for body should be easy now. But, be aware that you have to write the whole class in one go. Write the class header, the set_title and get_title methods, and then create your set_body and get_body methods. It may take you a couple of tries.
Files
As the Post class becomes bigger, you'll get tired of rewriting it every time you want to add a method. If you're tired already, that's a good sign. And besides, all that's in the REPL will be lost when we quit Python. We should start saving our work in files.
Python modules are simple text files, and you can use any text editor you want. As a programmer, you are going to spend most of your time with your editor, so take some time to choose one you really like and learn to use it well.
Emacs might not be the most beautiful editor, but for many programming tasks, it is awesome. (You could read that as “I don't like Emacs but it makes my life so much easier that I keep coming to it time after time”.) Installing Emacs from source is beyond the scope of this article. As usual, with programs that are so popular, your distribution is likely to provide it. In Debian and its derivatives try:
apt-get install emacs
For Gentoo, the counterpart is:
emerge emacs
To achieve the magic I am going to show here, you need python-mode.
In Debian:
apt-get install python-mode
In Gentoo:
emerge python-mode
Run Emacs. If you are serious about learning how to use it, now it is time to press Ctrl-H T, which in Emacs jargon means press Ctrl-H, release it and then press T. But, you can leave that for later, when you've finished reading this Linux Journal issue. For this article, I provide all the keystrokes you need.
Press Ctrl-X Ctrl-F (Ctrl-X, release, Ctrl-F) to visit a file. On the bottom of the Emacs window, you'll see the cursor waiting for you to type the path and filename. Type blog.py and press Enter. (Python modules should have the extension .py.) Now, you can start typing the Post class we programmed before. Emacs tries to be smart about indentation and places it where you are likely to want it. If you need a different indentation, simply press Tab and keep pressing it until you get the desired results.
On the top, you should have two menus: IM-Python and Python. The first one contains a list of classes and methods in the file you are editing. Click on Rescan if it doesn't show information you know is there. This is very useful when working with huge files. The second menu is even more useful, but explore and play with it later. For now, simply run Start interpreter... or press Ctrl-C !.
Suddenly the window is split, and you have an embedded Python interpreter below the file you are editing (Figure 2). And the fun is only beginning. Click on the file you are editing to set the focus on it. Run Import/reload file from the Python menu or press Ctrl-C Enter. Now, you're ready to test your code on the REPL, but be aware that you'll have to add blog. before the name of the class, Post, because now the class is in the module blog. See Figure 2 for further reference.
Figure 2. Testing the REPL
You can, of course, do the same without Emacs. But for that, you need to learn how Python modules and packages are made. Set PYTHON_PATH, an environment variable, accordingly, and use the built-in function reload. With Emacs, you'll find iterating between coding and testing the code to be very fast. This speed can improve your performance and make programming more fun. In fact, Lisp programmers have been advocating this way of working for more than two decades.
Special Methods
Having to create an object and then set each of its members is not pleasant. It takes a lot of lines and is very error-prone—did I remember to set the tags? There's a better way to do it—using the initialization method.
This special method is called __init__, and the parameters you define it to take have to be passed in the creation of the object. A possible initialization method would be:
class Post(object):
def __init__(self, title, body):
self.set_title(title)
self.set_body(body)
Simply add the __init__ definition to the file and reload it. We now can, and have to, set the title and body at initialization time:
/>>> cool = blog.Post("Cool", "Python is cool")
/>>> cool.get_title()
'Cool'
/>>> cool.get_body()
'Python is cool'
/>>>
Hint: to retrieve previous lines in the REPL inside Emacs use Alt-P.
There are other special methods. Remember how ugly it was to evaluate a Post itself? Let me remind you:
/>>> cool
We can solve that. There's another special method called __repr__, which is used to retrieve that string. Inside the Post class add:
def __repr__(self):
return "Blog Post: %s" % self.get_title()
Reload the file, the same way you loaded it previously, and evaluate a post:
/>>> ## working on region in file /usr/tmp/python...
/>>> cool
/>>>
Oops! That's not what we wanted. The problem here is that the cool object was created with an older version of the Post class, so it doesn't have the new method. That is a very common mistake, and not being prepared for it can cause a lot of headaches. But, simply re-create the object, and you are set:
/>>> ## working on region in file /usr/tmp/python...
/>>> cool = blog.Post("Cool", "Python is cool")
/>>> cool
Blog Post: Cool
/>>>
That's better.
What Now?
Easy—wait for the next issue of Linux Journal for Part II of this tutorial. If you really want something to do now, start learning Emacs.
Resources
Python: python.org
Python Download: python.org/download
Python 2.4.3: www.python.org/ftp/python/2.4.3/Python-2.4.3.tgz
José P. E. “Pupeno” Fern´ndez has been programming since...at what age is a child capable of siting in a chair and reaching a keyboard? He has experimented with more languages than can be listed on this page. His Web site is at pupeno.com, and he always can be reached, unless you are a spammer, at pupeno@pupeno.com.
Monday, May 5, 2008
Getting Wireless the NDISwrapper Way
Getting Wireless the NDISwrapper Way
Getting Wireless the NDISwrapper Way
December 1st, 2007 by Federico Kereki in
* HOWTOs
How to make NDISwrapper work with different wireless devices.
Using a wireless connection on Linux still is a dicey thing, and you must acknowledge the fact that you probably will have to go a non-open-source software direction, if you want to have any chance of getting connected. A lot of development is happening in this realm, but let's face it, making wireless devices work natively with open-source drivers is not something you can count on, at least for the time being.
What's the reason for this state of affairs? Just as with graphics cards, there are many factors. Vendors are quite interested in offering newer devices, but the majority of them do not care about Linux and have a Windows-only mindset. Furthermore, if you purchase a laptop, you may not even have the option to choose the card, so you have to make do with whatever you are dealt. Finally, developers cannot produce the needed software when manufacturers won't release the specs, APIs and so on for their products, and this documentation almost never is available.
Thus, unless you are quite lucky and happen to have one of the few open-source-supported options, NDISwrapper probably will be your best option; however, it's no surefire silver bullet. NDISwrapper tries to provide a wrapper around Windows drivers, so Linux can use them as if they were native ones. If you are lucky (okay, not so much lucky, as many drivers seem to work), your Linux machine will think it has perfectly fine drivers and will use them.
Though the program itself is open source, this usage of proprietary drivers is objectionable (maybe even not 100% legal?) and obviously not guaranteed. Furthermore, it's still a bit of a hit-or-miss, not very well documented and somewhat obscure method. There's no fancy graphic interfaces here, only old-fashioned command-line work, with many possibilities for mistakes. Taking all of this into account, it's no wonder many people become conscientious objectors of NDISwrapper or end up not being able to make it work.
Hopefully, in the near future, this will be a moot point, and we will have full open-source software for most, if not all, wireless cards. Certainly, such drivers exist for a select few. In the meantime, we have to make do with this sort of kludge—even though NDISwrapper is brilliant, it's a pity we have to rely on such a program—and wait for better times.
I recently got a somewhat old eMachines M5305 laptop, and as I had to work in a wireless-only office, I had no option but to get wireless going. In this article, I explain how I installed two different kinds of cards (USB and PCI) and got them to function with NDISwrapper. I won't dwell on the many other alternatives I tried (which got me nowhere) and focus on the NDISwrapper solution. Being purely practical, I needed my box to work—open-source software or not.
What Is NDISwrapper?
NDISwrapper stands for Network Driver Interface Spec Wrapper. A wrapper is a technical name for a piece of software that, loosely speaking, allows you to use something in a somewhat different way. Using Windows drivers under Linux certainly qualifies as “using something in a somewhat different way”.
You can find NDISwrapper in most modern distributions. Either use the provided repositories, or go to the NDISwrapper site (see Resources), download the source files and install them yourself. As a matter of fact, even though I use OpenSUSE 10.2 and NDISwrapper was readily available, I opted to go that route myself, so as to get the most up-to-date version. At the time of this writing, the current stable version stands at 1.47, and there is a 1.48RC2 release candidate in the works, but I decided to go with the former.
If you already had a version of NDISwrapper on your system, it might be best to uninstall it first (using the tools in your distribution—I used YaST), and then, as a common user, go to wherever you downloaded the file, and do the following:
tar zxvf ndiswrapper-1.47
cd ndiswrapper-1.47
make distclean
make
Finally, as root, type make install.
First Experiment: the USB Method
I went to a nearby computer shop and found a cheap, nice USB wireless adapter, Allied Telesyn's AT-WCU201G model, which supports speeds up to 108Mbps and is compatible with IEEE 802.11, 802.11b and 802.11g. Of course, today, speeds of 54Mbps are more common, so it's nice having an extra speed reserve resource.
Because this is a USB device, you can use the lsusb command to get the device ID. Insert the device at any USB slot, and do:
lsusb
Bus 003 Device 002: ID 3242:4001
Bus 003 Device 001: ID 0000:0000
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID 0000:0000
It's pretty clear our device's ID is 3242:4001. If you want more information—there's lots of it—about the device, try lsusb -d 3242:4001 -v, and you can verify the manufacturer, product code, serial number and so on. It's highly likely that other people already will have tried their hands at getting the device to work, so I Googled it on-line to see if there was any help or known good Linux drivers. I didn't find any useful references, but I went on ahead anyway—did you expect to give up so easily?
If you're following along, you will need a Windows XP driver, both the .inf and the .sys files. Fortunately, the product I chose includes a CD with several appropriate drivers, and it's just a matter of copying it to disk. Note that possibly not all Windows drivers will work or be stable. Sometimes you may discover you need to try alternate drivers to get your card to work. Also, the vendor might have included the needed files within an EXE file (try unzipping it and see if you get what you need) or a CAB file.
In my particular case, it was easy—just a matter of copying some files (atiwu.inf and atiwu.sys) and then typing ndiswrapper -i atiwu.inf, followed by ndiswrapper -m and then ndiswrapper -l to list all drivers. I got:
atiwu : driver installed
device (3242:4001) present
which shows that the device was recognized and the driver was installed. Now, it's time to let Linux know more about the new device. Because I use OpenSUSE, I opened YaST and went to Network Devices→Network Card→Add, and selected the following:
*
Device type: wireless.
*
Configuration name: 0 (so the device will be known as wlan0).
*
Hardware configuration name: static-2 (whatever).
*
Module name: ndiswrapper (most important).
Do not choose PCMCIA or BUS. Then, go through the next screens (pick either DHCP or an IP, specify your DNS servers and so on), until you come to the Wireless Device Settings form. There, you probably will have to set the following:
*
Operating mode: managed (ad hoc would work for a peer-to-peer network).
*
Network name: Guiamovil (the name of the network to which I wanted to connect).
*
Authentication mode: WPA-PSK (open would work for an unrestricted, unprotected, passwordless network).
*
Key input type and encryption key: whatever the network administrator tells you to use. (Of course, you don't need this for an “open” network.)
You can leave all these changes out, however, if you don't know to what network you will be connecting. Simply exit the program, save all the changes, and you will have device wlan0 available. We are almost there!
How Do I Connect?
If you made it this far, you can use the wireless tools, which include iwconfig and iwlist, to configure your device and search for available networks. (Of course, there are many similar tools, some even have graphic interfaces; look around if you so desire.) In my situation, I already knew the network to which I wanted to connect, but in other cases (Wi-Fi hot areas at airports, for example), you would need to look around to find one.
You can scan for networks with iwlist wlan0, and you'll get something like this:
wlan0 Scan completed :
Cell 01 - Address: 00:16:B6:DB:10:93
ESSID:"linksys"
Protocol:IEEE 802.11g
Mode:Managed
Frequency:2.437 GHz (Channel 6)
Quality:0/100 Signal level:-84 dBm Noise level:-256 dBm
Encryption key:off
Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 18 Mb/s
24 Mb/s; 36 Mb/s; 54 Mb/s; 6 Mb/s; 9 Mb/s
12 Mb/s; 48 Mb/s
Extra:bcn_int=100
Extra:atim=0
Cell 02 - Address: 00:4F:67:00:C1:AA
ESSID:"Guiamovil"
Protocol:IEEE 802.11g
Mode:Managed
Frequency:2.437 GHz (Channel 6)
Quality:0/100 Signal level:-67 dBm Noise level:-256 dBm
Encryption key:on
Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s
9 Mb/s; 12 Mb/s; 18 Mb/s; 24 Mb/s; 36 Mb/s
48 Mb/s; 54 Mb/s
Extra:bcn_int=200
Extra:atim=0
IE: WPA Version 1
Group Cipher : WEP-40
Pairwise Ciphers (1) : WEP-40
Authentication Suites (1) : PSK
Great, the connection I wanted is there (the second one, Guiamovil), and also another open, unprotected one called Linksys. (The unchanged name is a giveaway that the wireless router was installed without any specific configuration.)
If you would rather use the command line, iwconfig allows configuring the wireless device. Try iwconfig --help or man iwconfig to learn more about this. For example, I could get the same results that I did with YaST by typing:
iwconfig wlan0 essid "Guiamovil" mode managed key
"s:THESECRETPASSWORD" commit
Now, you simply can ping any site to check how the device is working or connect to the Web and surf along.
Second Experiment: the PCMCIA Method
A friend of mine loaned me a PCMCIA card, so I could test a different kind of device. I first thought about removing the USB driver and setup and then installing his card, but that would have been sort of lame. Anyway, just to make sure the card worked, I tried it, starting with:
ndiswrapper -r atiwu
rm /etc/modprobe.d/ndiswrapper
cd /path.where.you.downloaded.ndiswrapper
make uninstall
make clear
make
make install
to get back to square zero. I then inserted the card into its slot and used the lspci and lspci -v commands to learn more about it. I found the card at the bottom of the listing, and the second command produced its ID (11ab:1faa):
$ lspci
00:00.0 Host bridge: ATI Technologies Inc AGP Bridge
[IGP 320M] (rev 13)
00:01.0 PCI bridge: ATI Technologies Inc PCI Bridge
[IGP 320M] (rev 01)
00:07.0 ISA bridge: ALi Corporation M1533/M1535 PCI to
ISA Bridge [Aladdin IV/V/V+]
00:08.0 Multimedia audio controller: ALi Corporation M5451
PCI AC-Link Controller Audio Device (rev 02)
00:09.0 Modem: ALi Corporation M5457 AC'97 Modem Controller
00:0a.0 CardBus bridge: ENE Technology Inc CB1410 Cardbus Controller
00:0d.0 USB Controller: NEC Corporation USB (rev 43)
00:0d.1 USB Controller: NEC Corporation USB (rev 43)
00:0d.2 USB Controller: NEC Corporation USB 2.0 (rev 04)
00:0e.0 Ethernet controller: Broadcom Corporation BCM4401
100Base-T (rev 01)
00:10.0 IDE interface: ALi Corporation M5229 IDE (rev c4)
00:11.0 Bridge: ALi Corporation M7101 Power
Management Controller [PMU]
01:05.0 VGA compatible controller: ATI Technologies Inc
Radeon Mobility U1
02:00.0 Ethernet controller: Marvell Technology Group Ltd.
88w8335 [Libertas] 802.11b/g Wireless (rev 03)
$ lspci -s 02:00.0 -v
02:00.0 Class 0200: 11ab:1faa (rev 03)
This time, Googling for 11ab:1faa was lucky. I found several references saying that the card worked well, so I went ahead and installed it using the same method as with the USB card. It worked just fine—it was almost anticlimactic.
Now, back to the original idea of installing both devices at the same time. I wasn't even sure if NDISwrapper could handle two drivers at the same time, and when I searched the Web, I found nothing. Plowing ahead, I decided to experiment a bit. It turns out that the /etc/modprobe.d/ndiswrapper file read:
alias wlan0 ndiswrapper
So, I added an alias wlan1 ndiswrapper line to it, just to see if it made any difference. I went back to the beginning (uninstalled everything), rebooted (just in case) and started again. After setting up both devices, I tried iwconfig and happily saw that both interfaces were recognized. I could remove either of them, and the other still worked fine. I noted that the Allied Telesyn USB device was more sensitive—it could find more remote networks—than the Netgear card, but I guess that's reasonable, as the former is newer and could be expected to be more potent.
Conclusion
Even though wireless devices are still a low point for Linux, using NDISwrapper provides a good solution, if you are not dogmatic about proprietary binary drivers or using Windows software on your Linux box. I would prefer an OSS solution, but I can't wait for it. There are no guarantees, of course, but my experiments convinced me that getting wireless to work should be an easy job, and I was able to make two different devices work at the same time, which attests to the quality of NDISwrapper.