hardscrabble 🍫

By Max Jacobson

Psst. Check out my RubyConf 2017 talk, There are no rules in Ruby.

blog posts

shebangs and bins

19 Jan 2014

I had a thought the other night as I was falling asleep: I guess I could create my own shebang? I asked Siri to remind me later and fell asleep.

OK what’s a shebang? I’m going to try to explain my understanding of it step by step, forgive me if this is obvious or boring.

So let’s say you write a ruby program that looks like this:

# hello.rb
puts "hello world"

And you run it from the command line by writing ruby hello.rb and hitting enter.

Or maybe write basically the same thing in python like this:

# hello.py
print "hello world"

And run it from the command line by writing python hello.py and hitting enter.

And the same thing in CoffeeScript

# hello.coffee
console.log "hello world"

And run it by writing coffee hello.coffee and hitting enter.

Three different files are interpreted as three different programming languages by three different intrepreters. We indicate which language it is written in and should be interpreted as in two ways in those examples:

  1. which command we invoked (ruby, python, and coffee)
  2. the file extension (rb, py, coffee)

The file extension isn’t really necessary. See:

# hello.sandwich
puts "hello world"

? This file can be run with ruby hello.sandwich and it works totally fine. Probably continue to use conventional file extensions because they signal to your text editors and friends what type of file they are. But I think it’s kind of helpful to know that they’re not totally necessary or magical.

OK so what if you want to call this program without specifying the interpreter every single time? This is where shebangs come in:

#!/usr/bin/env ruby
# hello.sandwich
puts "hello world"

Now this is a file with a nonsense file extension but something new: the first line (the shebang) indicates which program is meant to interpret this file. Interestingly, it’s not a direct path to that binary file on your file system (which in the case of Ruby programmers who use rvm to switch between Ruby versions, is often changing – mine is currently at /Users/maxjacobson/.rvm/rubies/ruby-2.1.0-p0/bin/ruby but only because I’m checking out the newly released Ruby 2.1.0), but instead consults your environment and says “hey, I’m looking for something called ruby, who wants to handle this one?”

How do you call this file if you don’t specify the interpreter? Basically you just provide a path to the file, like this: ./hello.sandwich. You’ll probably get this error: bash: ./hello.sandwich: Permission denied. I’m sure there are very good security reasons for this but it’s kind of annoying honestly. You need to run chmod +x hello.sandwich which transforms the script from a generic text file to an “executable” file. If you do that, now you can run ./hello.sandwich and your program will run thru the ruby interpreter.

What is the ./ part? It’s kind of ugly, and it’s the next thing we want to get rid of. It’s a path to the file. The same way you might run cd .. to change directories up a directory, you can run cd . to remain in your current directory. I know programming has irrevocably ruined my sense of humor because I now find it funny that you can run cd ././././././. to remain where you are. So ./hello.sancwich is a path to a file in the current directory.

Let’s actually rename the file to have no file extension (mv hello.sandwich sandwich) and run it again ./sandwich. Still works. It’s totally possible to run this file without the ./, but we need to put it somewhere special. We need to put it on our $PATH, which is a list of all of the places on your computer you keep these executable files. To see your $PATH you can run echo $PATH and you’ll see them spat out all colon separated. You can move your file to one of those directories or create a new directory, probably located at ~/bin or ~/Dropbox/bin (via), and add it to your $PATH.

$PATH is a global variable in your command line environment and it can be edited like this: export PATH="whatever" but probably don’t do that because it will totally overwrite the variable in your current terminal session and you won’t have access to any of the executables you depend on and you won’t be able to do anything. The common way to append a directory to your $PATH is like this: export PATH=~/bin:$PATH, which sets the variable to ~/bin, a colon, and then everything that used to be in the variable. Perfect. If you want this directory to always be accessible, not just in the current terminal session, you should add that line of code to your ~/.bashrc or ~/.bash_profile file1 and it will be included in every new terminal session.

So, if your sandwich file is in a directory that’s on your $PATH, and it has the shebang that specifies its interpreter, you can now run simply sandwich from anywhere on your computer and it will call that program. It’s kind of amazing how it does it, right? You write the word sandwich, hit enter, and it looks thru a list of directories for a file with that name; when it finds one, it checks its permissions, then looks at the first line to figure out how to interpret the file; that line points to an environment-checking program and passes in the argument “ruby”2, which finds the correct ruby interpreter, and then calls it, evaluating the remainder of the program.

OK so that’s awesome. But what if I want to write this program:

#!/usr/bin/env sandwich
# grilled_cheese
  ingredients: [
    "american cheese",
    "some sprinkles of gorgonzola",
    "maybe some sweet chili sauce",
  directions: [
    "get ingredients",
    "assemble a sandwich",
    "melt a little butter in a pan",
    "put the sandwich in the pan",
    "apply some downward pressure",
    "apply some downward pressure",

OK so I put “sandwich” in my shebang instead of a real programming language. What. Haha.

Let’s make the grilled_cheese file executable and put it in our bin, and then run grilled_cheese. We want to confirm that it’s looking up and finding the sandwich program and running it. When we see “hello world” again, we know it is. But it’s not making a sandwich! But then, how can it? Our code isn’t even being evaluated; if it were, we would probably have an error because the make_sandwich method is not availabe in the Ruby Standard Library (unfortunately).

Let’s edit our sandwich program to get a sense of what it knows about our grilled cheese program.

#!/usr/bin/env ruby
# sandwich
puts ARGV.inspect

Now when I run grilled_cheese, instead of seeing “hello world” I see ["/Users/maxjacobson/bin/grilled_cheese"]. Ahaa! Just because we invoke the sandwich program from the grilled cheese program doesn’t mean we will automatically evaluate the grilled cheese code. We just pass the filename as an argument, much like when we run ruby hello.rb. It’s a lot like if we ran sandwich grilled_cheese.

OK so knowing that, maybe our sandwich file should look something like this:

#!/usr/bin/env ruby
# sandwich

def make_sandwich(options)
  options[:ingredients].each do |ingredient|
    print "Finding #{ingredient}..."
    sleep 1
    print " done!\n"
  options[:directions].each do |step|
    print "Doing #{step}..."
    sleep 1
    print " done!\n"

eval File.read(ARGV.first)

So, this defines our missing method, takes in the file as an argument, reads the file as a string, and then evaluates the file with eval. OK so the only thing I know about eval is that you probably should never use it because it’s dangerous af. But maybe this is the kind of context where it makes sense?

I’m sure there are actually practical reasons to use custom-written programs in the shebangs of other custom-written programs and if you think of any please let me know.

  1. Unfortunately still don’t have a really solid understanding of the difference between those. 

  2. env is available to use from the command line as well: try running env ruby -v 

updating a jekyll/github blog from the iPad, or anywhere, without touching git

12 Jan 2014

UPDATE April 2015: Here’s a much, much better way to do it: </2015/how-to-jekyll-from-ios/>

First of all: if anyone subscribes to my RSS feed, I apologize for flooding it today with test posts as I futzed with this.

Today I wrote my first Editorial workflow and also my first Python script. Fun day!

Editorial is a power user text editor for the iPad. Before today I never really used it but I think I’ll use it more now.

This project was inspired by a recent episode of the tech podcast Core Intuition. They were discussing blogging software. One of the hosts, Daniel Jalkutt, makes the Mac blogging app MarsEdit, and they’ve both been around long enough to have really interesting thoughts on the space. MarsEdit allows bloggers to post to most of the major blog systems but not static blogs like Jekyll (what I use for this blog), Pelican, Octopress (which is built on Jekyll), Roots, or any number of other, similar projects.

Which is understandable because those don’t really expose an API for it to integrate with. But what if they did? (haha)

I’m going to step away from discussing the broader context of static blog tools and focus specifically on Jekyll blogs deployed to GitHub pages, which is the bulk of my experience.

Here’s how I generally write and publish posts, before today:

  • clone or pull the git repo
  • use poole to run poole draft "Title Of Post", which reates a new markdown file for me
  • use vim to write the post
  • serve the blog locally to make sure it looks right
  • when I’m done, run poole publish _drafts/title_of_post.md, which moves the file into the _posts directly and inserts the current time into the configuration at the top
  • use git to commit and push the post up to GitHub, which will re-compile the site using jekyll build

I can do this easily on the computer and it’s pretty nice. I can do this on the iPad using Prompt to SSH into a DigitalOcean server and it’s okay. I can do the same on my iPod Touch and it’s kind of awkward and overly small.

There are a few major advantages to having a static blog. To me, the main boon is the lack of a database. It’s definitely an advantage that there’s no server-side computation going on – the HTML is available to serve immediately after being requested – but maybe it’s a disadvantage that there’s no server-side API. In theory, a static blog API could do several things:

  • create posts
  • update posts
  • delete posts
  • render posts as JSON

What I’ve created today only does the first thing. It’s pretty simple. It would need to do all of those, and probably much more I’m not currently imagining, to integrate with something like MarsEdit. But it’s enough to do something pretty cool: now I can write posts in Editorial on my iPad (I’m writing this one that way) which has a browser built-in for research and an embarrassment of possibilities for extensibility because it’s somehow snuck the Python scripting language into Apple’s walled garden.

Here’s a demo video (sorry it’s so long):

Updating a Jekyll Blog with Editorial from Max Jacobson on Vimeo.

Here’s the source of the Rails project that serves as a Jekyll API: maxjacobson/jekyll_api. Here’s the Editorial workflow which will currently only work for me, because I’m not sure the best way to share the source for something like this that’s not just a script, but a chain of macros and scripts.

Actually, here’s the most-relevant chunk of the workflow, my first python script, somewhat out of context:

#coding: utf-8
import workflow, requests

script_params = workflow.get_parameters()

auth_token = script_params["auth_token"]
blog_id = script_params["blog_id"]

post_slug = workflow.get_variable("slug")
post_text = workflow.get_variable("text")

params = {
  'auth_token': auth_token,
  'post[slug]': post_slug,
  'post[text]': post_text

response = requests.post("http://pairwith.maxjacobson.net:3000/blogs/" + blog_id + "/publish", params=params)

if response.status_code == 200:
  workflow.set_output("Successfully posted")
  workflow.set_output("Something went wrong")

As I mention in the video, in theory I can deploy this app to an actual production environment (note the weird URL if you like) and allow other people to use it. I probably won’t.

Alright now I’m going to press “Post”. I’m kind of nervous! Wish me lu