Enough Sed

My goals were modest, or so I thought. Fiddle with my wmii configuration, add a battery charge percentage indicator to the status bar, run through lesswatts.org and make some readjustments to save some power, and that should about do it. Six hours of the day were blocked off for pure and simple “me time.” The cast of the musical I directed several months ago were coming to pick up the T-Shirts I had printed for them, and I gave them six hours to do so. It turns out that over two days I got only managed to do two of the things I had intended to.

The first thing to tackle was wireless. I had decided to switch to wicd instead of network-manager. Most people seemed to make the transition smoothly but, of course, not me. Something happened. Or didn’t happen. And whatever it was prevented wicd from connecting to or scanning for wireless networks unless network-manager had been run and had connected to a network. It seemed to have a mind of its own. So what do I do when a Linux distribution seems to have a mind of its own, the forums don’t give me an adequate (or any) response, I’ve crapped up the partition with unused packages (mostly KDE 4 related,) and a few hours of fiddling leads me no where but in circles? Reinstall the entire distribution, of course. Besides, my 10.04 install was just a dist-upgrade from 9.10, so the configuration files seemed to be a mess. Reinstalling fixed the problem entirely.

Next up, after the fresh install, was to tackle this stupid tap-to-click thing. Why on Earth anyone would want their trackpad to click randomly while they try to navigate their Desktop is beyond me, but it drives me absolutely insane. Why it’s enabled by DEFAULT is a complete mystery as well. Gsynaptics is great, but I don’t want to fiddle with trackpad settings, I want to scratch a spell into the back of my computer so it never does it again. But I settled for just changing the configuration files. In older Ubuntu versions you had to fuss with HAL, which is a bit of a pain, and has never worked for me. In Ubuntu 10.04, you use udev rules. It seems more straight forward, but I have a better way. In /etc/X11/xorg.conf.d you will find some configuration files. One of them will say something like 10-synaptics.conf. Edit that, and at the bottom of the top section add

Option “MaxTapTime” “0”

Log out, log back in, and the damnable tapping behavior will disappear.

Next, I wanted to be able to see what my remaining battery life was. Now I’m almost 100% sure that my solution is not the most efficient one, since it takes approximately 2 seconds for my code to check. But I wanted to learn about pipes and sed, and I wanted to do all this in bash with one line of code (hence the rather punny title of this blog post.) First I’ll show you the code, then I’ll explain it.

echo `grep ‘remaining capacity’ /proc/acpi/battery/BAT0/state | sed ‘s/[^0-9]//g’`/`grep ‘last full capacity’ /proc/acpi/battery/BAT0/info | sed ‘s/[^0-9]//g’` | bc -l | cut -c 1-3 | sed ‘s/^\.//;s/\./0/g;s/$/\%/’

It took me several hours to build that one command. I started hardly knowing anything about regular expressions. I’ve typed that line so many times that I can remember it by heart – I just typed it from memory. The echo and grep stuff is pretty simple: anything in `these quotes` get’s evaluated, and the returned value replaces the quotes. The first grep looks for the remaining capacity in the battery section of proc’s acpi diectory, and the second one looks for the last full capacity. The tricky parts are the sed commands. Each grep command is piped into a sed command, which removes all non-numeric characters. [0-9] matches all numeric characters.  Putting a carrot: ^ in front of the 0 inside the brackets matches everything but numeric characters. ‘s/regex/replace/’ will look for ‘regex’ at the beginning of a line, and replace it with ‘replace’ if it finds it. To make sed do that for every character, you add a ‘g’ at the end like so: ‘s/regex/replace/g’ So to look for every non-numeric character and replace it with nothing (i.e. delete it,) you do:

sed ‘s/[^0-9]//g’

If you just ran the echo stuff, you would get something like “40437/50800” depending on your remaining battery capacity (proc gives measurements in mWh.) This is piped to bc -l, which divides the two, and gives .79600393700787401574. You could leave it like that, but it’s not very pretty, and I still hadn’t learned all I wanted to about sed. So this result is piped to cut, which will trim the output into .79. For some bizarre reason cut starts with 1 and not 0, so the command is cut -c 1-3, which cuts everything but the first character to the third.

Now we want to do two things: we want to get rid of the decimal point, and put a parenthesis. But wait! That works if the battery isn’t fully charged, but what if the value is 100%? In reality, I never asked this question. It occurred to me right as I was just trying something to get my wireless working. I did whatever I was trying, saw it fail, and as I looked down at my status bar, I saw this: .0% Two failures in under a second. Pretty impressive. Originally I was getting rid of the decimal point by cutting characters 2 to 3, but when bc -l returned 1.0, the command cut off the 1 and added a %. FAIL. This is a problem for sed to handle. What we want to do is remove a decimal if it occurs in the first position, but convert any other decimal points to a zero. Then we add the percent. The sed expression goes like this:

sed ‘s/^\.//;s/\./0/g;s/$/\%/’

Wow, that’s a lot of gibberish. Let’s break it down.

s/^\.//

s/\./0/g

s/$/\%/

In the first one, the ^ character tells sed that what we’re looking for comes at the beginning of the line. The backslash escapes the period. Huh? In the language of regular expressions, a period means “any character.” So we have to tell sed that we actually mean “period.” To do this, we do something called escaping, which means that we put a backslash to tell sed that the next character is to be read as is, and not interpreted to mean something else. The backslash is pretty much the universal escape character. sed will replace anything that matches what the expression in between the first and second slashes with whatever is in the second and third slashes, and since there’s nothing between the second and third slashes, the period will be deleted. Any leading decimal points are now gone.

The second expression is more or less the same, but it doesn’t have the ^, and it has a g. So this expression looks through the entire line for a period, and replaces it with 0 (because there’s a 0 between the second and third parenthesis. Not bad. But I think I can make this a little more efficient by making the expression more complex so that sed doesn’t have to search the entire line each time:

s/\([0-9]\)\./\10/

Uh… huh? When you put escaped brackets around something \( \), it tells sed to remember what that matches the pattern between those brackets. To use whatever it finds, you just type \1. The pattern is looking for one numeric character, so let’s say that sed gets 4.0 as input. This will recognize and store the 4, and then it will find the period, which is part of the regular expression, but not part of the pattern to be remembered. It will then replace what it found: 4. with the expression between the next two slashes, which is \10. That means that it will replace using the number it remembered, and add a 0, which will give us 400. But I just used 4 to prevent confusion with \1, in reality we’ll never have 400% battery charge, so we’ll find 1.0 and get 100.

Finally, the last expression simply replaces the end of the line, which contains nothing, because it’s the end of the line, with a % sign. The $ character means that the pattern should occur at the end of the line, and since the pattern is nothing, the expression refers to the nothing at the end of the line, essentially.

I have learned two very valuable lessons from all this. First, I’ve gained a lot of experience with sed. Second, I’ve learned something that I’ve long suspected: you don’t learn computer science by copying solutions you find on the internet, or by asking for help all the time, or by fixing a problem in a complicated manner because the elegant solution takes too much effort to understand. You learn computer science by finding the most elegant solution to a problem even if it takes you a few hours and a few dozen tries. You learn computer science by saying, “I wonder what would happen in I did this,” and then trying things out for a while. You have to take your time, and keep trying until you’re confident that you’ve solved the problem in the most efficient, effective manner. This is what it’s about.

Note: Yes, yes, it’s Thursday, and I was supposed to post this yesterday; I’m out of my rhythm and all that. So I’m planning to write a make-up post this weekend. Deal? Ok.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s