Monday, November 12, 2007

Unix Power User Overkill And IRB

Here's a line of actual shell code:

! !! | g? ?

The first ! is a custom shell prompt, defined in my profile. The !! is a Unix shell special variable, which means "last command." The | is a pipe, which means "pipe output of preceding into following." Essentially it's Unix-ese for an arrow. The g? is my alias for grep. (Soon to be replaced on my box with ack.) The ? is the only literal character in the whole thing.

What I'm doing here is I've just run svn stat, and gotten back too many files - I have lots of changes to check in. All I want to see is new files I haven't added yet, so I can add them. Those appear with a ?, so I'm saying "do that svn stat again, but grep it for question marks."

What's cool about this is its incredible brevity. What's not cool about it is the minimal legibility. Unix ninjitsu is so terse it's wonderful, but it sometimes makes Perl regexes look like "see Spot run" by comparison.

I've almost got this much terseness in IRB, but not quite. I don't currently have an IRB equivalent to !!. I have one for !$, which means "the last term in the last command," in _, which is enabled if you just do

IRB.conf[:EVAL_HISTORY] = 1000

...(or any number) in your .irbrc. I also have Ben Bleything's utterly essential history code, and an equivalent to this Unix trick:

!523

...which means "repeat command number 523," in the history code's h! method.

Ben's code is already freely available. I think I can hack an IRB !! pretty quick if I find the time for it. I'm working on releasing all this stuff as a gem, but between work, this blog, my new podcast, my acting class, following the writer's strike, and a new workout regimen which essentially crippled me after only two days, it's still not quite ready for prime time.

Similarly, there's no real equivalent to | g? yet in IRB. If you have a line of code which returns a string, it's very easy to do _.grep(/some*regex/). However, a very common idiom in my own Unix shell usage is h?, which I guess I've elevated from an idiom to a term, but which is essentially just history | grep. That doesn't exist in my IRB yet, but I think adding that will also be pretty easy. It's effortless to get the history, thanks (again!) to Ben's code, but I think (iirc) Ben's code writes to $stdout with puts, so I'll need to write another method to actually grep the history. The cool part is, I will be able to keep my shell term for it, h?, so that part at least will be transparent for me.

If I could just figure out how to enable Unix shell tab-completion from within IRB system() calls and backticks, I'd never use bash again.

Update: Ben corrected me - it turns out h! actually already gives you !! if you call it with no arg. That's pretty awesome. I think IRB might be the biggest strength of Ruby for me personally. It leverages Unix. If you're used to thinking in Unix, IRB makes a lot of sense, and being able to do !! in Ruby kicks much ass.