Quick Intro

Rules of the road:

* Everything you enter is pure Ruby. When you type, you are typing pure Ruby.
* When in doubt of what something is: use #inspect by doing command[:inspect]
* #[] allows you to run methods of Executables, Pipes, and StringMethods
  without accidentally running them.

Sample commands and shizzle:

ari: ~/src/chitin % ls
ari: ~/src/chitin % 5 + 4
ari: ~/src/chitin % ll = ls -:al
ari: ~/src/chitin % hg.stat
ari: ~/src/chitin % hg.stat | wc | L {|i| i.size }
ari: ~/src/chitin % hg.stat | wc | L {|i| i.size } > '/tmp/test.out'
ari: ~/src/chitin % 44 - 2
ari: ~/src/chitin % _ + 5
ari: ~/src/chitin % wget "http://bigbad.evils.in
ari: ~/src/chitin % _ # run the same command again

Look! Thanks to Mon_Ouie, it even has syntax highlighting on the line as you type! Hooray! Thank you, monsieur!

For the most part, you can just use chitin as you would bash or any other shell.

Why should you use Chitin?

* You can do simple arithmatic on the command line without having to switch
  interfaces.
* The power of Ruby in a shell
* The command utility of a shell in Ruby
* No underlying shell usage
* A much more programmatic shell
* Prevents against accidental `rm -rf /usr /local/bin` like that one thing
  we all saw on Reddit.
* Syntax highlighting while you type
* Makes for a great Christmas present
* Text processing is SO. MUCH. EASIER.
* Great library for calling executable files from your own program without
  shelling your soul or shelling out.

The Library

Buckle up, kids:

include Chitin

Real simply, here’s how you create a reference to a file with Chitin:

f = FileObject.new "/bin/ls"
f = F "/bin/ls"

and a directory:

d = Directory.new "/bin"
d = D "/bin"

Which is sweet and all, but you kinda need read and write priveleges in order to read and write to it. And if you’re writing to /bin/ls, you’re a bad person.

So we can make it an executable really easily:

f = Executable.new "/bin/ls"

By default, it reads and writes from and to, respectively, empty pipes. So to make it work in a standard manner, we need to set it up to use STDIN, STDOUT, and STDERR.

f.in.reopen  STDIN  # f.in is the input to f
f.out.reopen STDOUT # f.out is the output of f
f.err.reopen STDERR # f.err is the error of f

We can easily run this:

f.run

SWEET!

A more complicated example of “cat /tmp/cool | wc -l”:

f = Executable.new "/bin/cat", "/tmp/cool"
g = Executable.new "/bin/wc", "-l"

Now we link them together (something like f | g)

g.in.reopen f.out

And make the overall input and output visible to us

f.in.reopen  STDIN
g.out.reopen STDOUT

and run them

f.run # since these are asynchronous
g.run # this seems like an ok thing to do

That’s a lot of work, though. Luckily, we can simplify it all:

STDIN > f | g > STDOUT

What’s that? You have another function called h?

h = Executable.new "/bin/cat"
pipe = STDIN > f | g | h > STDOUT

And then you can run the pipe simply:

pipe.run

Easy peasy, right? But what if you want to run the exact same command again? No worries:

pipe.run

It automatically resets itself internally after being run.

You can even use ruby code:

STDIN > f | gsub(/asdf/, "LAWL") > STDOUT

And you can chain it simply:

STDIN > f | gsub(/asdf/, "LAWL").chomp > STDOUT

You can even set up a pipe to return a random ruby object of your choice:

STDIN > f | split.size > STDOUT

Although you have to run it with ‘pipe.raw_run`.

If you have your own method and you want to take the result of something as an input, you can use a special proc to make it happen:

STDIN > f | L {|i| my_method(i) } > STDOUT

And let’s say that you want it all into a file. No problemo, broski.

STDIN > f | L {|i| my_method(i) } > '/tmp/lolwut'

Dis ish goes both ways:

'/tmp/my_input' > f | L {|i| my_method(i) } > '/tmp/lolwut'

SILENCE ALLLLLLLLLLLLLLLLLLLL

NULLIN > f | L {|i| my_method(i) } > NULLOUT ^ NULLERR

Git on mah level, son.

The Shell

Chitin is such named because it is the material that comprises the exoskeleton shells of insects. Yes, I know insects suck, but chitin is a pretty cool word, especially when you learn that it’s pronounced chitin and not chitin.

By default, chitin gives you a funky prompt that’s big like yo mamma. The reason the default prompt is so AWESOME is so that it doesn’t look identical to my bash prompt (leading me to get confused) but still provides useful information. Before we continue our tutorial, let’s change that. Add the following bit of code to ~/.chitinrc

def prompt
  "#{ENV['USER']}: #{short_pwd} % "
end
  1. Well done! You’ve been promoted to tenderfoot. I’m proud of you.

So now let’s get going with this thang. Try out some random math equation:

ari: ~/src/chitin % 4 + 5
 => 9
ari: ~/src/chitin % (1 + 3 + 4  +65 + 7 +345) / 23
 => 18.4782608695652

NB: integer division is off by default because it sucks and always has sucked. If I’m cranking out some quick maths, I don’t want to deal with the fact that 3/4 = 0. NIQUE LA POLICE WOOT WOOT!!!!!

So ladida, you’re doing some math, this could be IRB with a few changes. HOLY BATMAN! LOOK AT THAT SHIZNIZZLE YOU JUST DID!!! Oh, you mean this bit?

ari: ~/src/chitin % ls
NOTES   README  TODO    bin     lib

Yeah, that’s right. We just ran a command. And guess what? We didn’t have to start a shell underneath to get it to work! We also didn’t use any system calls because that would be hell to get right for every system. Though it might be neat for a future version. Note to self…

But that’s not all! Let’s make ruby do some heavy lifting for us:

ari: ~/src/chitin % ls | L {|s| s.split }
 => ["NOTES", "README", "TODO", "bin", "lib"]

Chyeah son, it even returned us a ruby object.

What Chitin as a shell does is it automatically does the STDIN > … > STDOUT boilerplate that you saw in the first section. It also automagically runs your pipe, calling #run in the general case and #raw_run if it’s supposed to return ruby.

To earn your second class rank, you need to do a few things. First, you must demonstrate proficiency with the bowline knot and clove hitch. These are just good life skillz. Second, you must solve a problem for “your friend Jimmy”.

Jimmmy has a pokemon folder on his computer that he doesn’t want his dad to see. Unfortunately, Jimmy doesn’t know about cryptography and wants to give you (hah, as if you and Jimmy aren’t the same person) a task to solve with Chitin. Help him hide his pokemon folder from his dad who likes to cd and ls around!

ari: ~/src/chitin % ls
NOTES   POKEMON    README  TODO    bin     lib
ari: ~/src/chitin % ls | L {|s| a = s.gsub /pokemon/i, "digimon"} | cat
NOTES
digimon
README
TODO
bin
lib
ari: ~/src/chitin % def ls; raw_command('/bin/ls') | L {|s| s.gsub /pokemon/i, "digimon" } | cat; end
 => nil
ari: ~/src/chitin % ls
NOTES
digimon
README
TODO
bin
lib

Save that in you ~/.chitinrc and your dad won’t find out about your pokemon collection.

Congratulations! You are now a second class scout!

Alright, hotshot. Let’s see if you’ve got what it takes to be first class. Your boss rolls into your office and asks to speak with you. You know it couldn’t be about your pokemon collection because you fixed that in attaining second class; now he just thinks you’re a nerd for having a 40GB digimon collection. So bossman rolls in and says “Yo dawg, delete every file and folder in a directory.” Naturally, he decides not to open up a GUI and do that himself using the wonders of modern technology (the mouse). You look that cat dead in the eye and say “YES SIR LET’S DO DIS THANG!”

ari: ~/src/chitin % Dir['pokemon/*'].each {|f| rm f }

But since I’m a sick bastard, I showed you code that DOES NOT WORK. Before I get into why it doesn’t work, lemme show you code that DOES work, first:

ari: ~/src/chitin % Dir['pokemon/*'].map {|f| rm f}

The difference here is that we used #map instead of #each. Why?

#each is a function that returns self after running the block. Since an executable is just a Ruby object that is only run when told to, those blocks will never run the code you want. In order to run the code, we use #map to return the executables we want to run in an array. Chitin knows that whenever it sees an array of executables, its job is to execute them.

But let’s get back to the original problem, which was removing a buttload of files:

ari: ~/src/chitin % rm *Dir['pokemon/*']

The built-in Ruby * (glob) operator enables us to do batch functions as an afterthought. It is like ‘rm “file1”, “file2”, …`.

Sweet deal brah, you’re a second class scout. To become a first class scout, you need to install something. Luckily, Rosy the Nosy Neighbor from down the street (guess who just watch “Trapped in the Closet” – all fucking 23 videos) wants you to install this C program that she wrote so she can monitor your keystrokes. Here comes you to the rescue! OH MAN that rhymed. Time to give up ‘gramming and move to lyrical wordsmithing.

First, you gotsta download the codez:

ari: ~/src/chitin % wget "http://supersecret.com/rosys_stuff.tar.gz

Simple enough, you’re just running the ‘wget` command. Now, unzip it to remove it from its archived format. Use tab completion to make your life simpler! Or not…

ari: ~/src/chitin % tar.zxf "rosys_stuff.tar.gz"

Easy there, cowboy. You have no idea what Rosy the Asshole just sent you. Let’s try it again except THIS time, use the “v” flag for “tar”.

ari: ~/src/chitin % rm -:rf, "rosys_stuff
ari: ~/src/chitin % tar.zxvf "rosys_stuff.tar.gz"

So here we learned a few things about Chitin and how it plays well with 40 fucking years of bash-style syntax and history. Most obviously, -:rf takes a symbol and translates it into ‘-rf’. These three are all equal:

-:rf == -'rf' == '-rf'

You want two dashes? Sweet deal brah.

--:rf == --'rf == -'-rf' == '--rf'

Next in the commands, we use tar. But we don’t just run tar. We run tar.zxvf. tar refers to the command itself, and then calling any methods on it send the method name directly to the executable. Thus, ‘tar.zxvf’ becomes ‘tar zxvf’. We do this here because tar does NOT want any dashes.

So back to the installation process:

ari: ~/src/chitin % here 'configure', :prefix => '/Users/ari/local'
ari: ~/src/chitin % [make, make.install]

‘here` is the method we use to refer to an executable file relative to the current directory. We’re using hash notation in configure to automatically prepend two dashes to ‘prefix’ (‘–prefix’); the whole line is translated as “./configure –prefix /Users/ari/local”. We’re also using the #& operator to do each command only if the previous one succeeds.

In lieu of using ‘here`, which can understandably get a little tedious, we can do two other things:

ari: ~/src/chitin % raw_exec 'configure', :prefix => '/Users/ari/local

And if you didn’t want to include any arguments:

ari: ~/src/chitin % '.'/'configure'

For the second one using String#/, it can’t take any arguments because otherwise it wouldn’t be valid Ruby. I’m still trying to figure out a better way to make use of it.

So now that we have this all, let’s try archiving it all up again.

ari: ~/src/chitin % tar.zcf 'compiled.tar.gz' => 'rosys_stuff'

Much better.

Color Guard: Working with Flags

Here’s a quick synopsis of flag equivalencies from bash-style flags to Chitin. The first on the left is the bash, and everything else is Chitin:

-h          == -:h == -'h' == '-h'
-f file     == :f => file
-help       == -:help ==  -'help' == '-help'
--d         == --:h == --'h' == '--h'
--pref file == :pref => file

You should be able to get the idea after that. The important things to note are how hashes are treated. If the key is one character long, it gets one dash. If the key is two or more characters long, it gets two dashes. This makes dealing with Java programs really fucking annoying.

String Methods

One of the points of using Ruby in a shell is that you can use Ruby. For instance:

ari: ~/src/chitin % ll | split("\n")

However, what if there’s an executable on your path entitled “split”? Then the executable would be the one to be run. However, I really, really, really want to use a few String methods such as #gsub and #split regardless of whether there is an executable named as such. So, there’s the Kernel::string_method function that takes a string method and makes it a PRIORITY method. This means that the Ruby method will take precedence over any executables found.

The string methods which take precedence over executables are:

gsub
split
size
pack
unpack

Globbing: Globlins and Hobgloblins

Globbing is simple in Chitin: there is none. However, that’s ok because globbing is a really really cheap hack. So here’s what Chitin offers in its stead:

ari: ~/src/chitin % D('.').map {|f| echo f } # D stands for Directory
ari: ~/src/chitin % all.map {|f| echo f }    # all is an alias to D('.')
ari: ~/src/chitin % echo *all                # Ruby's natural glob!

Now, all and D(‘.’) will only get the files and directories in the current directory. What if you wanted to see just how far the rabbit hole went?

ari: ~/src/chitin % all.down.map {|f| echo f }
ari: ~/src/chitin % echo *all.down

#down will go from the directory it’s called on allllll the way down to Davey Jones’ locker.

Kewl Features

Did you know?

* A FULL RUBY INTERPRETER!
* Tab completion that respects spaces in strings!
* Bash's ESC-. to place the last argument!
* Syntax highlighting as you type!
* ^q quotes the word just behind your cursor
* I think this train might ACTUALLY get to Chicago on time!

Thanks

A huge thank you to Roger Pack. You know why.