...making Linux just a little more fun!
By Mike Orr ("Sluggo")
"Woomert, Woomert!" Frink Ooblick ran into the room and shut the door behind him. He was clearly out of breath.
"What is it, Frink?" said Woomert Foonly. "You look like you've seen a ghost."
"There's a guy outside named Sluggo. He says he's gonna kick my Perl ass."
"Ass?" Woomert was puzzled. "But you don't own a donkey."
"He means arse," explained Frink.
"Oh, in that case he must be talking about a camel. A camel's behind. What do you know about this Sluggo?"
"He's the one that moved into the apartment on the corner. He's big and bald and has a menacing black bomber jacket. There's a tattoo on his arm of a snake. And there's a patch on his jacket of a camel with a slash through it."
"A Python bigot, huh?"
"Looks like it. He says he can beat our wimpy-ass Perl one-liners."
"Well, we've got him beat already there." Woomert laughed. Everybody knows there are no Python one-liners except 'print "Hello, spam world!"'"
"He wants to enter your POLOTM contest. And---" Frink stopped.
"And what, Frink?"
Frink replied quietly, "He... he said you're a dirty communist spy."
"Hey, I bathe every day! Well, well, let's put him in the contest." Woomert mused. "Tell him I'll give him a Perl example and he'll have to come up with a Python example that's just as elegant. But we'll have to find a judge. He certainly won't accept me as an unbiased judge." He thought and thought, and then banged his fist on the table. "I know! Elbert Arkleberry. He's widely known as being language neutral. I bet he'll do it. But you, Frink..." He put his hand on Frink's shoulder. "You'll have to explain and defend the Perl example yourself."
"Yikes!" said Frink and jumped. "You want me to defend Perl code against that animal? He'll tear my head off."
"I think it will be a good learning experience for you. Remember how worried you were about dealing with that hot babe client, and then it turned out to be no big deal. Why don't you go outside right now and tell this Sluggo to meet us tomorrow at ten behind the Quick-E-Mart."
"Well, OK, Woomert, if you think so. But... can I borrow your dark sunglasses? I think they'll give me courage."
"Sure, Frinko." He rummaged around and found the glasses, which he hadn't worn in three months. "Here you go."
Frink put the glasses on and left the room a bit more boldly than when he had entered.
The winter sun was clear but cold on the small circle that had gathered behind the Quick-E-Mart. Frink was sitting on the curb (or kerb as Woomert spelled it) with his laptop open on his knees. Sluggo was sitting on the curb a few blocks away. He didn't have a laptop, but Judge Arkleberry had brought an old 486/75 for him to use. The judge himself, an ex football player, sat between the two sides to prevent any arguments from boiling over. Woomert stood in front of them with a stack of printouts.
Sluggo looked over at Frink and squinted his eyes nastily. Frink swallowed and tried to return the gaze. Woomert watched the two and chuckled. "All right, let's begin," he said, and the staring contest was over. They both looked at Woomert. He showed them a printout. "Here's the first one," he said. On the printout was written:
PERL PROGRAM: perl -wpe's/at/op/g' INPUT INPUT FILE: A cat sat on a mat with a hat. OUTPUT: A cop sop on a mop with a hop.
Woomert said, "Frink, explain it."
"The -w flag prints warnings about constructs that look like common mistakes in Perl. The -p flag means run each input line through the expression and print the result. Actually, what it prints is the value of the default variable $_, but for a one-expression program like this it's essentially the same thing. -e means you're giving the program on the command line. s is a regular-expression substitution. g (global) means replace all occurrances on the line, not just the first."
"Thank you, Frink. Sluggo?"
Sluggo typed for a couple minutes on his laptop, and then came up with this:
% python -c \ > "import sys; print sys.stdin.read().replace('at', 'op')," <INPUT A cop sop on a mop with a hop.
[We split the line because some browsers and printers don't display wide <PRE> lines well. Sluggo actually typed one line without the backslash.]
"Zounds!" said Woomert. "I thought Python's separate import statement made any non-trivial one-liner impossible. I forgot you can have two commands on one line with a semicolon. All right, explain your program, Sluggo."
"sys is the module that has several miscellaneous system services. In this case we're using sys.stdin, which is the standard input filehandle. We don't need sys.stdout because print prints there by default. .read is a method that returns the entire file content as a string. Since it's now a string, we can use the string method .replace on it, which substitutes every 'at' with 'op'. The comma at the end prevents print from adding a newline, so the result is closest to the original. Python doesn't have an equivalent of Perl's -w flag, but it doesn't need it. The syntax is more straightforward and strict, so most of the constructs -w would complain about are errors in Python."
Sluggo didn't mention that Python does have warnings and a -W flag, because most Pythoneers don't use it. The built-in warnings are few in number and are related to much less drastic situations. -W exists in case your program raises your own custom warnings, or if you really want to get nagged about depreciated features.
The judge looked at the program, and said after a moment, "Sneaking that import statement onto the same line is OK; we couldn't very well have a contest without that, since Python puts basic services into libraries. .read is OK, even though it slurps the entire file at once rather than line by line. In this trivial case it doesn't make a difference either way. It just means you won't want to use it on a file that's several megabytes big. But I'll have to reject .replace. That's a literal string substitution and doesn't give the flexibility of a regular expression. You can have a second try if you wish."
Sluggo frowned and started an editor. In a couple minutes he showed another program.
% cat program.py import re, sys; print re.sub(sys.argv, sys.argv, sys.stdin.read()), % python program.py at op <INPUT A cop sop on a mop with a hop.
"OK, explain that one," said Woomert.
Sluggo explained, "It's the same as the previous example but with a regular expression. re.sub is a function that does the equivalent of Perl's s///g. It automatically replaces all occurrances so you don't need a g flag. sys.argv is an list-- I mean, an array-- of the command line arguments. sys.argv is the program name itself, so we skip that. sys.stdin.read() turns the input file into a string, the same as above."
"But the command-line syntax isn't the same," Frink objected. "Even if we allow the program to be in a file rather than inline."
"Python doesn't need the s///g syntax. It's easier on the user if they don't have to get the slashes right, especially if you're embedding slashes in one of the parts. I know you can do s!!!g in Perl, but my way is even more convenient."
The judge spoke. "I like the way the Python program has a simpler command-line syntax. But the program is much more verbose than the Perl one. I call it a tie."
"The judge has spoken," declared Woomert. He held up another printout.
perl -i wpe's/at/op/g' *
"I'll have Frink demonstrate it."
Frink showed on his laptop how the program edits several files in place. "That's what the -i option does. Other than that it's the same."
"OK, Sluggo, it's your turn," said Woomert.
Sluggo turned a deep red and didn't move. Woomert said, "Sluggo, is something wrong?"
Sluggo sat silent a long moment. Finally he confessed, "That's the one case where I still use Perl."
Frink was shocked. "Does that mean you forfeit the contest?"
"I guess so." Sluggo looked at Frink for a second, then looked away.
Judge Arkleberry cleared his throat. "We'd still like to see how you'd do it in Python."
Sluggo remembered that there was a fileinput module, but he couldn't remember the syntax. So he spent a few minutes looking through the Python Library Reference on the judge's laptop, and then wrote:
% cat program.py import fileinput, re, sys for lin in fileinput.input(sys.argv[3:], inplace=1): print re.sub(sys.argv, sys.argv, lin), % cat INPUT A cat sat on a mat with a hat. % cat INPUT2 tit tat % python program.py at op INPUT INPUT2 % cat INPUT A cop sop on a mop with a hop. % cat INPUT2 tit top
Sluggo added, "I've tried and tried to think of a way to do that Perl example consisely in Python, but I haven't. Perl sucks rocks, but that one case makes it worth it. There is one thing I'm frustrated about it, though. Sometimes you want it to interactively ask you which matches to change, like vim does but with multiple files. Vim lets you search and replace in multiple files, but you gotta do ':w :n, arrow up a few times, enter' at the end of every file, and that gets tiresome. I've thought about writing a program to do that but haven't gotten around to it. Maybe in next month's PyOLOTM, although it certainly won't be a one-liner, so I'll have to think up a new title for the column."
The judge looked around. "Did Mr. Bint ever show up?"
Sluggo whispered to Frink, "Who's Mr. Bint?"
Frink whispered back, "He's this guy who thinks C is the only language worth learning, and wishes we didn't publish so many Perl and Python articles because it distracts people from learning the One Really Useful Language in depth. Nobody has seen him since April. He's a homeless guy in England, and he types his entire article at the public library."
"That's pretty f*ing amazing!" Sluggo said. "The library here gives you only 45 minutes a day on the Internet, and all you get is a lousy web browser, Word and Excel. It would take forever to write an article that way. And how would you test the program?"
Sluggo closed the judge's notebook and stood up to leave, but Woomert cleared his throat. "Uh, Sluggo, there is one remaining thing we've got to settle right now." He put on his dark sunglasses.
Sluggo's jaw tensed and he eyed Woomert closely, ready in case he made a sudden move. "What's that, Wal-Mart?" he asked warily.
"I do bathe once a day," said Woomert, ignoring the pun on his name.
"OK, you're a clean communist spy."
"One whom Thomas Adam, the Linux Weekend Mechanic, has suggested should stand for the office of British Prime Minister," said Woomert, with a flourish and a tip of his hat.
"He means 'run for the office of'," explained Frink to Sluggo.
Sluggo held out his hand to Frink and said, "The best tool for the job." Frink shook his hand and echoed, "The best tool for the job." Sluggo slapped him on the back and said, "You're all right, even if do have an idiot name." Frink replied indignantly, "Hey, it's not as bad as 'Woomert Foonly'."
A few hours later, Woomert was at his office when the door opened again, and Frink and Sluggo emerged chatting noisily.
"It looks like you made a new friend, Frink," observed Woomert.
"Sluggo and I are gonna go beat up some Visual Basic weenies," said Frink excitedly, putting on the old steel-toe boots Sluggo had given him (two sizes too big) and stuffing a lead paperweight in his pocket.
"Yeah, I know where they hang out, under the bridge on the edge of town," explained Sluggo.
"But what about all that best-tool-for-the-job stuff this morning? Have you forgotten it already?"
"Oh, we were only talking about real languages," said Frink, dashing out the door with Sluggo.
"Have fun!" Woomert called after them with some trepidation.
"We will!" shouted Frink in the distance.
Mike is the Editor-in-Chief of Linux Gazette. You can read what he has
to say on the Back Page of many issues. He has been a Linux enthusiast
since 1991 and a Debian user since 1995. He was SSC's web technical
coordinator 1999-2003, which means he got to write a lot of Python scripts.
Now he's involved in three free software
projects for Python (Cheetah,
YAML), writes unittests and programs for a
Webware e-commerce site, and edits LG from his home.
Non-computer interests include wrestling, ska and oi! and ambient music, and
the international language Esperanto. He's been known to listen to Dvorak,
Schubert, Mendelssohn and Khachaturian too.