So, in my previous hack I showed how to rename lots of files with little effort. Let’s remind ourselves how the one-liner looks:

for f in *.doc ; do mv $f ${f%.doc}.txt ; done

Imagine that you haven’t done that kind of transformation a thousand times and so feel a bit apprehensive about diving right in. The standard way to get around that is to pre-pend the command with echo:

for f in *.doc ; do echo mv $f ${f%.doc}.txt ; done

Now, instead of performing a lot of renames, your loop will just print out the commands it would do if the echo was not there. When you’ve looked over the output and satisfied that it will do what you want, you have to options. You can either edit the command to remove the echo, or you can pipe the result to sh -; the latter being my favourite:

for f in *.doc ; do echo mv $f ${f%.doc}.txt ; done | sh -

The pipe, for those who haven’t encountered it, means that the output of the command to the left of it is used as input for the command to the right. The sh command is just a shell[1], and invoking it with the argument ‘-’ means that it should read commands from its standard input and execute each line. Simple!

[1] - similar to the one you’re typing these commands into; on your system, it’s probably an alias for bash or zsh.

I was astounded to find out a couple of days ago that one of my colleagues was not aware of shell parameter expansion. He’d been confronted with the following problem:

Given a directory full of files, write a shell one-liner to rename all the ones ending in .doc such that they now end in .txt.

I almost choked on my drink as I heard suggestions involving Perl. No need to complicate things. Here’s how to solve the task using parameter expansion (both Bash and Zsh certainly have these):

for f in *.doc ; do mv $f ${f%.doc}.txt ; done

Now I know that my previously mentioned colleague won’t need the following explanation (he’s already dived into the Bash man page by now, I suspect), but I’m feeling wordy for a change. Here’s the above line with white space added for readability:

for f in *.doc ; do
    mv $f ${f%.doc}.txt
done

On the first line we use a shell glob (the *) to select all the files in the current directory that ends in .doc. For each iteration of the for loop, the variable f contains the name of one of those files. The third line just says that we’re done with the looping, so all the really interesting bits are happening in the second line.

That line says: rename[1] the file named $f (in shell programming we put a $ in front of the variable name when it can be confused with a literal string) to the result of the following expression: ${f%.doc}.txt. Of that ${f%.doc} is the parameter expansion part. From the zsh man page:

${name%pattern}
${name%%pattern}

If the pattern matches the end of the value of name, then substitute the value of name with the matched portion deleted; otherwise, just substitute the value of name. In the first form, the smallest matching pattern is preferred; in the second form, the largest matching pattern is preferred.

What that means is that if the string in the variable f contains .doc at the end of it (which we know that it does) strip it off. This will be the case for all the files, since our for loop only iterates over files with this ending. The dot over the i is that we simply append .txt.

There are loads of other parameter expansion tricks you can do, including the following, which should feel familiar to Perl people: ${f:gs/foo/bar/}—substitute with the content of the variable f, but with the string “foo” replaced by “bar” everywhere it occurs in f.

Enough from me. Dive into the Bash/Zsh man page already!

[1] — on Unix a rename is just a move[2] to a new name.
[2] — Unix geeks are lazy typists, hence “move” was shortened to “mv”.

Set CFBundleVersion

March 25, 2007

setCFBundleVersion.pl is a quick hack to update the CFBundleVersion in Info.plist. I invoke it from a Makefile that also builds the documentation and release tarball, so I’m sure all the version numbers agree. I coded it quite defensively and believe it to be well-behaved, but don’t blame me if it causes demons to fly out of your nose.

Update, 30th April 2007: Updated the script to deal with version numbers of the form x.y.z.

Quiet please!

March 3, 2007

In January I bought a guitar and a small practice amp. As I noted back then the amp has lots of effects which saves me buying an array of pedals. It has one problem though: it is very loud. And by that I don’t mean that it goes to 11, but that it goes from zero to hero already around 2: below is too quiet, above too high, and the optimal setting in the middle is too hard to find.

There’s simply not enough (useable) dynamic range. I could turn down the input volume from my guitar, but that changes the sound unfavourably (I get lower signal-to-noise ratio, and many of the effects rely on high input level to work satisfactorily).

To solve the problem I measured the resistance through the speaker to 6.2 Ohms, hooked a 57.4 Ohm resistor in series with it (for a total of 63.6 Ohms) and a 8.2 Ohm resistor in parallel with that. Substituting 63.6 and 8.2 for R1 and R2 in 1/R1 + 1/R2 = 1/R, the formula for calculating the combined resistance in a parallel circuit, and solving for R gives a combined resistance of about 7.3—close enough for my purposes.

The result? Most of the output from the amp now bypass the actual speaker, shunted off into a resistor where it is converted to heat rather than sound; the volume dial has a much greater effective range, making it much easier to adjust to a sensible level; and my neighbours are much happier.

I broke down and signed up to the Xcode mailing list in order to ask how you can set the default prefix for bundle identifiers, as the lazyweb proved spectactularly useless in helping me with that problem. Turns out it’s not exactly dark magic; I got a response with detailed instructions within minutes—once I managed to get the question across correctly.

Here’s what I ended up doing. This is more for my own reference than anything else (so I can just copy and paste the next time) but other people might find it useful too:


mkdir ~/Library/Application\ Support/Apple/Developer\ Tools/Target\ Templates
cp -r /Library/Application\ Support/Apple/Developer\ Tools/Target\ Templates/Cocoa ~/Library/Application\ Support/Apple/Developer\ Tools/Target\ Templates
cd ~/Library/Application\ Support/Apple/Developer\ Tools/Target\ Templates/Cocoa/
perl -pi -e ’s/com.yourcompany/org.brautaset/g’ *

jerakeen pointed me to a post showing how you could Generate Simple Bar Charts From a MySQL Prompt. It’s a neat trick for quickly getting a feel for the distribution of values in a database. The post is MySQL centric, but you can do the same thing in any database. PostgreSQL’s REPEAT is very particular about being passed an INT, however, so you may have to cast the second argument:

SELECT foo_id, REPEAT('#', COUNT(*)::INT) FROM bar GROUP BY 1;

Perl testing trick

November 23, 2006

Have you ever found yourself creating a t/lib directory for modules used during testing? I know I have. Sometimes it’s tempting to just put these modules in your regular lib directory, to avoid cluttering your test files with boilerplate (say t/lib/Foo.pm is the test module you want to load):


use FindBin qw( $Bin );

use lib "$Bin/lib";

use Foo;

Here’s one solution: bastardise the name of your test module somewhat, and you can cut those three lines down to one:


use t::lib::Foo;

Perl will search for modules in the current working directory and will find your test module. The added benefit this has, of course, is to make it abundantly clear that this module is there for testing only.