|
|
- 4. script
- =========
-
- Back in chapter 1, I said that "the way you use the computer is often just to write
- little programs that invoke other programs". In fact, we've already gone over a
- bunch of these. Grepping through the text of a previous chapter should pull
- up some good examples:
-
- <!-- exec -->
-
- $ grep -E '\$ [a-z]+.*\| ' ../literary_environment/index.md
- $ sort authors_* | uniq -c
- $ sort authors_* | uniq > ./all_authors
- $ find ~/p1k3/archives/2010/11 -regextype egrep -regex '.*([0-9]+|index)' -type f | xargs wc -w | tail -1
- $ sort authors_* | uniq | wc -l
- $ sort colors | uniq -i | tail -1
- $ cut -d' ' -f1 ./authors_* | sort | uniq -ci | sort -n | tail -3
- $ sort -u ./authors_* | cut -d' ' -f1 | uniq -ci | sort -n | tail -3
- $ sort -k1 all_authors.tsv | expand -t14
- $ paste firstnames lastnames | sort -k2 | expand -t12
- $ cat ./authors_* | grep 'Vanessa'
-
- <!-- end -->
-
- None of these one-liners do all that much, but they all take input of one sort
- or another and apply one or more transformations to it. They're little formal
- sentences describing how to make one thing into another, which is as good a
- definition of programming as most. Or at least this is a good way to describe
- programming-in-the-small. (A lot of the programs we use day-to-day are more
- like essays, novels, or interminable Fantasy series where every character you
- like dies horribly than they are like individual sentences.)
-
- One-liners like these are all well and good when you're staring at a terminal,
- trying to figure something out - but what about when you've already figured it out and
- you want to repeat it in the future?
-
- It turns out that Bash has you covered. Since shell commands are just text,
- they can live in a text file as easily as they can be typed.
-
- learn you an editor
- -------------------
-
- We've skirted the topic so far, but now that we're talking about writing out
- text files in earnest, you're going to want a text editor.
-
- My editor is where I spend most of my time that isn't in a web browser, because
- it's where I write both code and prose. It turns out that the features which
- make a good code editor overlap a lot with the ones that make a good editor of
- English sentences.
-
- So what should you use? Well, there have been other contenders in recent
- years, but in truth nothing comes close to dethroning the Great Old Ones of
- text editing. Emacs is a creature both primal and sophisticated, like an
- avatar of some interstellar civilization that evolved long before multicellular
- life existed on earth and seeded the galaxy with incomprehensible artefacts and
- colossal engineering projects. Vim is like a lovable chainsaw-studded robot
- with the most elegant keyboard interface in history secretly emblazoned on its
- shining diamond heart.
-
- It's worth the time it takes to learn one of the serious editors, but there are
- easier places to start. Nano, for example, is easy to pick up, and should be
- available on most systems. To start it, just say:
-
- $ nano file
-
- You should see something like this:
-
- -> ![nano](images/nano.png) <-
-
- Arrow keys will move your cursor around, and typing stuff will make it appear
- in the file. This is pretty much like every other editor you've ever used. If
- you haven't used Nano before, that stuff along the bottom of the terminal is a
- reference to the most commonly used commands. `^` is a convention for "Ctrl",
- so `^O` means Ctrl-o (the case of the letter doesn't actually matter), which
- will save the file you're working on. Ctrl-x will quit, which is probably the
- first important thing to know about any given editor.
-
- d.i.y. utilities
- ----------------
-
- So back to putting commands in text files. Here's a file I just created in
- my editor:
-
- <!-- exec -->
-
- $ cat okpoems
- #!/bin/bash
-
- # find all the marker files and get the name of
- # the directory containing each
- find ~/p1k3/archives -name 'meta-ok-poem' | xargs -n1 dirname
-
- exit 0
-
- <!-- end -->
-
- This is known as a script. There are a handful of things to notice here.
- First, there's this fragment:
-
- #!/bin/bash
-
- The `#!` right at the beginning, followed by the path to a program, is a
- special sequence that lets the kernel know what program should be used to
- interpret the contents of the file. `/bin/bash` is the path on the filesystem
- where Bash itself lives. You might see this referred to as a shebang or a hash
- bang.
-
- Lines that start with a `#` are comments, used to describe the code to a human
- reader. The `exit 0` tells Bash that the currently running script should exit
- with a status of 0, which basically means "nothing went wrong".
-
- If you examine the directory listing for `okpoems`, you'll see something
- important:
-
- <!-- exec -->
-
- $ ls -l okpoems
- -rwxrwxr-x 1 brennen brennen 163 Apr 19 00:08 okpoems
-
- <!-- end -->
-
- That looks pretty cryptic. For the moment, just remember that those little
- `x`s in the first bit mean that the file has been marked e**x**ecutable. We
- accomplish this by saying something like:
-
- $ chmod +x ./okpoems
-
- Once that's done, it and the shebang line in combination mean that typing
- `./okpoems` will have the same effect as typing `bash okpoems`:
-
- <!-- exec -->
-
- $ ./okpoems
- /home/brennen/p1k3/archives/2013/2/9
- /home/brennen/p1k3/archives/2012/3/17
- /home/brennen/p1k3/archives/2012/3/26
-
- <!-- end -->
-
- heavy lifting
- -------------
-
- `okpoems` demonstrates the basics, but it doesn't do very much. Here's
- a script with a little more substance to it:
-
- <!-- exec -->
-
- $ cat markpoem
- #!/bin/bash
-
- # $1 is the first parameter to our script
- POEM=$1
-
- # Complain and exit if we weren't given a path:
- if [ ! $POEM ]; then
- echo 'usage: markpoem <path>'
-
- # Confusingly, an exit status of 0 means to the shell that everything went
- # fine, while any other number means that something went wrong.
- exit 64
- fi
-
- if [ ! -e $POEM ]; then
- echo "$POEM not found"
- exit 66
- fi
-
- echo "marking $POEM an ok poem"
-
- POEM_BASENAME=$(basename $POEM)
-
- # If the target is a plain file instead of a directory, make it into
- # a directory and move the content into $POEM/index:
- if [ -f $POEM ]; then
- echo "making $POEM into a directory, moving content to"
- echo " $POEM/index"
- TEMPFILE="/tmp/$POEM_BASENAME.$(date +%s.%N)"
- mv $POEM $TEMPFILE
- mkdir $POEM
- mv $TEMPFILE $POEM/index
- fi
-
- if [ -d $POEM ]; then
- # touch(1) will either create the file or update its timestamp:
- touch $POEM/meta-ok-poem
- else
- echo "something broke - why isn't $POEM a directory?"
- file $POEM
- fi
-
- # Signal that all is copacetic:
- echo kthxbai
- exit 0
-
- <!-- end -->
-
- Both of these scripts are imperfect, but they were quick to write, they're made
- out of standard commands, and I don't yet hate myself for them: All signs that
- I'm not totally on the wrong track with the `meta-ok-poem` abstraction, and
- could live with it as part of an ongoing writing project. `okpoems` and
- `markpoem` would also be easy to use with custom keybindings in my editor. In
- a few more lines of code, I can build a system to wade through the list of
- candidate files and quickly mark the interesting ones.
-
- generality
- ----------
-
- So what's lacking here? Well, probably a bunch of things, feature-wise. I can
- imagine writing a script to unmark a poem, for example. That said, there's one
- really glaring problem. "Ok poem" is only one kind of property a blog entry
- might possess. Suppose I wanted a way to express that a poem is terrible?
-
- It turns out I already know how to add properties to an entry. If I generalize
- just a little, the tools become much more flexible.
-
- <!-- exec -->
-
- $ ./addprop /home/brennen/p1k3/archives/2012/3/26 meta-terrible-poem
- marking /home/brennen/p1k3/archives/2012/3/26 with meta-terrible-poem
- kthxbai
-
- <!-- end -->
-
- <!-- exec -->
-
- $ ./findprop meta-terrible-poem
- /home/brennen/p1k3/archives/2012/3/26
-
- <!-- end -->
-
- `addprop` is only a little different from `markpoem`. It takes two parameters
- instead of one - the target entry and a property to add.
-
- <!-- exec -->
-
- $ cat addprop
- #!/bin/bash
-
- ENTRY=$1
- PROPERTY=$2
-
- # Complain and exit if we weren't given a path and a property:
- if [[ ! $ENTRY || ! $PROPERTY ]]; then
- echo "usage: addprop <path> <property>"
- exit 64
- fi
-
- if [ ! -e $ENTRY ]; then
- echo "$ENTRY not found"
- exit 66
- fi
-
- echo "marking $ENTRY with $PROPERTY"
-
- # If the target is a plain file instead of a directory, make it into
- # a directory and move the content into $ENTRY/index:
- if [ -f $ENTRY ]; then
- echo "making $ENTRY into a directory, moving content to"
- echo " $ENTRY/index"
-
- # Get a safe temporary file:
- TEMPFILE=`mktemp`
-
- mv $ENTRY $TEMPFILE
- mkdir $ENTRY
- mv $TEMPFILE $ENTRY/index
- fi
-
- if [ -d $ENTRY ]; then
- touch $ENTRY/$PROPERTY
- else
- echo "something broke - why isn't $ENTRY a directory?"
- file $ENTRY
- fi
-
- echo kthxbai
- exit 0
-
- <!-- end -->
-
- Meanwhile, `findprop` is more or less `okpoems`, but with a parameter for the
- property to find:
-
- <!-- exec -->
-
- $ cat findprop
- #!/bin/bash
-
- if [ ! $1 ]
- then
- echo "usage: findprop <property>"
- exit
- fi
-
- # find all the marker files and get the name of
- # the directory containing each
- find ~/p1k3/archives -name $1 | xargs -n1 dirname
-
- exit 0
-
- <!-- end -->
-
- These scripts aren't much more complicated than their poem-specific
- counterparts, but now they can be used to solve problems I haven't even thought
- of yet, and included in other scripts that need their functionality.
|