An Event Apart 09

I attended the recent An Event Apart conference in Boston. An Event Apart is aimed at designers who build websites, and has a standards-oriented slant.

I am not a designer, but I have to play one from time to time. I don’t plan to make designing websites my primary focus, but some basic skills is necessary these days. Also I think the web might not be a passing fad, so investing in some expertise in that realm would probably pay off in the long run AEA seemed like a good way to jump-start that area of my professional skill set.

The talks were very high quality across the board, and the topics ranged from design psychology to CSS nuts and bolts. I found Jason Santa Maria and Dan Cederholm to have the most useful technical content. Whitney Hess’s DIY UX talk and Kristina Halvorson’s Content Strategy talk both had great information from a less technical, but still development-related standpoint. From a general-interest perspective, Jared Spool on Amazon and SimpleScott on the Obama campaign website were extremely interesting.

Beyond the talks, the conference itself was a great experience. The presenters were very down to earth, and I met a lot of cool people. The designers didn’t even seem to mind that I wasn’t “one of them”.

A few personal key takeaways:

  • “Websites don’t need to look the same in every browser.” This was the mantra of the conference. We were made to chant it over and over until it permeated our souls (just kidding (sorta)).
  • Grid-based layouts are your friend.
  • Fluid layouts are cool.
  • Sketch. “Sketchbooks are not about being a good artist, they’re about being a good thinker.” – Jason Santa Maria.
  • “The behavior you’re seeing is the behavior you’ve designed for.” – Joshua Porter
  • Charge for the value you bring to a client, not the hours you work.

I didn’t take many pictures but instead outsourced them via the AEA Boston 09 Flickr pool.

Finally, I wanted to put some of my newfound skills and excitement to use. Without further adieu, I give you: doesthissitejustshowonebigword.com.

And the winner is… wait… what?

The iPhone app we built for Wolfgang’s Vault, Concert Vault (iTunes link), was awarded Best iPhone/iPod touch application by Macworld UK.

This is quite the pleasant surprise. I had no idea it was even being considered. It’s an honor to be chosen out of all of the nominees, which are all great apps. I use several of them myself.

Working with Wolfgang’s Vault has been a great experience and we’re excited to continue building apps to deliver their extraordinary content on the iPhone platform.

WWDC 09 Post Mortem

WWDC 09 was my second WWDC, after first attending in 2008. I would have liked to do a more well-constructed breakdown of this year’s conference, but time has been scarce, and I didn’t want the information to get (more) stale. Instead, I give you a brain-dump of some of my experiences:

  • Keynote

    • Cheaper, better laptops? Yes please.
    • Snow Leopard for $29 is great.
    • Boos at AT&T were funny (and warranted).
    • “Find My Phone” was the surprise hit.
    • Too much time in demos (a third of which didn’t work!).
    • Scott Forstall seemed like a stronger presenter than Phil Schiller.
    • My prediction the new iPhone’s video chat feature would be demoed by calling Steve Jobs was wrong.
  • Events and Parties

    • sfMacIndie was worthwhile – bumped into some old friends and made some new ones.
    • First Stump the Experts. Fun, but I don’t know if I’d do it again.
    • Those WebKit guys and girls can throw a good party.
    • The Big Nerd Ranch was, of course, a lot of fun.
    • Beer Bash: Cake was good, but the food, not so much.
      • “Don’t bring hot dogs to a sausage party.” – @themartorana
  • Travel & Lodging

    • First Virgin American experience was positive, though the best part is their attitude, not the in-flight entertainment or wifi.
    • The InterContinental was really convenient and pleasant. Internet will cost you though.
  • Food & Drink

Whew. I think that’s all the non-NDA-encumbered information I have for now. Again, WWDC proved to be a good experience. If you develop Mac or iPhone software, and can afford it, it is definitely worth your time.

Finally, it was awesome to travel with so many local friends this time (even the smelly ones) and of course, make some new ones. That’s what WWDC is really all about, anyway.

Oh, and the few photos I took can be found here.

Now let’s just hope the videos come out more promptly this year. ;-)

Another “Open Terminal Here” Toolbar App

In Learning the Terminal on the Mac – Part 4, I mentioned a mini-application called OpenTerminalHere that opens a new Terminal in the same directory as the current Finder window. Well recently Dan Benjamin linked to another, nearly identical application called cdto.

Both toolbar applications work as advertised, but cdto takes the extra step of clearing the scrollback buffer after changing directories, yielding a slightly cleaner workspace. cdto is also hosted on Google Code, so it’s easy to grab the source on hack on it.

Enjoy.

Terminal Quickie: Kill ‘Em All

More often than I’d like, my Mac gets into a very unhappy state. Running applications won’t respond, but they won’t force quit either. New applications won’t launch, and the unresponsive applications block a proper system restart.

Usually I want to cut my losses, and get my machine back into a good state as soon as possible. As a last resort, I could do a hard restart with the old hold-the-power-button-down-for-5-seconds trick, but then I have to wait for the machine to reboot and risk damaging my filesystem.

There is an alternative method that will kill all of your processes, responsive or not, and get you back working much more quickly – as long as you still have shell access:

$ kill -9 -1

Warning: this really will kill all your running processes immediately, without saving any data. Use wisely.

Discussion

So what’s going on here? Basically, the kill (man page) is used to terminate processes. Usually it is used to kill a single process, like so:

$ kill 1234

where 1234 is the process id. The process id can be obtained a number of ways, but the most common is with the ps (man page) command.

The kill command also lets you specify a signal to send to the process. By default, it sends the TERM signal, which ask the program to terminate. However, the TERM signal isn’t always enough. Sn unresponsive program often won’t be able to react to the signal, and programs can even choose to ignore the signal completely.

Luckily, the KILL signal does not suffer from the problem. When this signal is sent, the operating system will stop the process dead in its tracks, whether it wants to stop or not. The KILL signal is very powerful, but don’t only use it as a last resort because it does not allow processes to save data, close resources, and otherwise exit cleanly.

You can send the KILL signal to a process like this:

$ kill -KILL 1234

Or use the numeric value for the signal, which is 9:

$ kill -9 1234

There is one final piece to the puzzle. Notice that in the original command we pass a -1 as the process id. This is a special argument that tells kill to send the signal to all processes belonging to you (or every process on the system, if you’re the super user).

Basically, kill -9 -1 is a quick way or killing every process belonging to you. Running this on a OS X system is a quick a dirty way to clear your environment and start fresh. Remember, this will kill all processes immediately – without saving data – and log you out, so use with care.

Adding Debug-only Preferences in iPhone Applications

Recently I bumped into an iPhone application configuration/deployment problem, and thought someone else might benefit from my findings.

Motivation and Goals

Here’s the basic scenario: I wanted to expose certain options in my iPhone application that were only available in Debug builds. One choice would be to add the ability to change the options in the application itself, and use the usual conditional compilation (#ifdef DEBUG…) techniques to hide or expose them based on build configuration.

However, this would require adding a whole bunch of custom UI in my application, which didn’t really seem appealing. The application was already using the standard iPhone application Setting system, which is driven by simple plists. I thought to myself, boy it would be nice if my Debug-only options could be automagically added to Settings bundle at build time.

For example, the standard settings pane would look like this:

NoDebug.png

And debug-only options would be added automatically, to produce this:

HasDebug.png DebugPane2.png

I’m a believer in the DRY principle, so I wanted to avoid duplicating and information at all possible. Also I wanted to “set it and forget it” and not require any manual build steps.

After some experimentation, trial, and error, I arrived at the following solution:

  1. Store Debug settings in a separate child pane. The Settings system allows for child panes of the root pane. To keep the main settings and debug settings separate, we keep them on different panes.
  2. Add an reference to the child pane from the root pane at build time. To keep the main root settings clean, we’ll inject the reference to the debug clild pane at build time.

Create the Property List

First, add a Settings Bundle to your project if you haven’t already. Choose File -> New File… -> Settings -> Settings Bundle.

Next we need to add a second plist for our Debug settings. Xcode doesn’t allow you to create a new plist inside the Settings bundle (or any bundle for that matter), so we’ll have to create our Debug.plist by some other means. If you’re familiar with the shell, the easiest way is to navigate to Settings.bundle inside your project and simply duplicate the main Root settings to create Debug settings.

cp Settings.bundle/Root.plist Settings.bundle/Debug.plist

If you prefer to use Finder, navigate to Settings.bundle and then right- or control-click and choose Show Package Contents. Then option-drag to duplicate Root.plist and rename it to Debug.plist.

Once the file has been created, collapse and expand the Settings bundle in your projects Groups & Files section to refresh it’s contents. It should look like this:

AddDebugSettingsExample - Debug.plist .png

Note, you can name the child settings plist something other than “Debug” if you want. We’ll see how to do that in the next section.

The Injection Script

The purpose of the injection script is to add a child pane option to an existing settings plist file. It uses the built in PlistBuddy utility to do all the real work.

The addDebugSettingsChild.sh takes two arguments. The first is of course that path to the target plist file. The second is the name of the new child pane, or ‘Debug’ if unspecified.

Here’s the script:

#!/bin/sh

# addDebugSettingsChild.sh
#
# Simple script to inject a Debug menu in an iPhone Settings plist.
#
# created 10.15.2008 by Andy Mroczkowski, mrox.net

THIS="`basename $0`"
PLISTBUDDY="/usr/libexec/PlistBuddy -x"

set -e

if [ -z "$1" ]; then
    echo "Usage:"
    echo "   $THIS plist_file [child_pane_name]"
    echo "   - If unspecified, child_pane_name is 'Debug'"
    exit 1
fi

if [ ! -e "$1" ]; then
    echo "[$THIS] file not found: '$1'"
    exit 2
fi

if [ ! -z "$2" ]; then
    CHILD_PANE_NAME="$2"
else
    CHILD_PANE_NAME="Debug"
fi

TARGET="$1"
echo "[$THIS] adding '$CHILD_PANE_NAME' child to: $TARGET"

$PLISTBUDDY -c "Add PreferenceSpecifiers:0 dict" "$TARGET"
$PLISTBUDDY -c "Add PreferenceSpecifiers:0:Type string 'PSGroupSpecifier'" "$TARGET"

$PLISTBUDDY -c "Add PreferenceSpecifiers:1 dict" "$TARGET"
$PLISTBUDDY -c "Add PreferenceSpecifiers:1:Type string 'PSChildPaneSpecifier'" "$TARGET"
$PLISTBUDDY -c "Add PreferenceSpecifiers:1:Title string '$CHILD_PANE_NAME Settings'" "$TARGET"
$PLISTBUDDY -c "Add PreferenceSpecifiers:1:File string '$CHILD_PANE_NAME'" "$TARGET"

Download

Creating the Build Step

We’ve created a Debug settings child pane in our settings bundle, and a script to add an option to access our child pane from the root pane. Now we just have to tie the pieces together in Xcode’s build settings.

Copy addDebugSettingsChild.sh to your project’s folder. You don’t need to add it to the project itself. Next add a new “Run Script Build Phase” to the target for your application. Order it so be comes after the “Copy Bundle Resources” phase, and name it something more descriptive if you’d like. I called mine “Run Debug Settings Script”.

Debug.plist - AddDebugSettingsExample-2.png

Next we have to make the our new Run Script phase actually do something. Basically, we need to call the addDebugSettingsChild.sh if we’re building with the “Debug” configuration, and also set up the input and out files so Xcode knows when to run the script and when not to. Let’s set up the input and output files first.

Add the path to your Root settings plist as an input file. If you used the default names and locations, this should be:


$(SRCROOT)/Settings.bundle/Root.plist

The file we will be altering will be in the built products area, not the project source root, so the output file should be:


$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/Settings.bundle/Root.plist

Finally create the body of the script. I only want to add my extra settings if I'm building the "Debug" configuration, so my script looks like this:

if [ "$CONFIGURATION" == "Debug" ]; then
    sh addDebugSettingsChild.sh "$SCRIPTOUTPUTFILE_0"
fi

You may notice that the script's input is specified as the output file. This is not a mistake. The addDebugSettingsChild.sh script modifies the specified plist in place, and we want to alter the one in our built-products area, not the source tree.

Again it's important to specify the input and output files. If you don't, Xcode may run the script on every build, which will result in repeated Debug child pane choices in our main settings plist.

Once you're done, it should look like this:

Run Script Phase for “AddDebugSettingsExample” Info.png

Conclusion

And that's it. You can now add edit the Root and Debug plists and they will be updated appropriately and everything will be happy. Well, mostly happy (see Caveats). I hope you found if useful. If not, I'm sorry. Here's some pictures of Robocop on a Unicorn to cheer you up.

Sample Project

You can download a sample project with all the above steps already completed here.

Caveats and Future Work

  • Be careful when switching configurations and using Build and Run. The products in the build folder for each configuration should always be correct, but Xcode won't necessarily re-install the right Setting bundle if it doesn't detect a change. The workaround is to just build clean and re-build when switching from Debug to Release or vice versa. Any suggestions on how to improve this are welcome.
  • Child pane options are always injected at the top. I'd prefer if I could add the "Debug Settings" option at the end of the main preferences, but I could not find a simple way to append an element to the end of an array with PlistBuddy. I considered using PlistBuddy to loop through the array to determine the size, and then use that to append it to the end, but I didn't want to add all that complexity to the script for a minor issue just yet.
  • Debug.plist still exists, but is inaccessible, in Release builds. This method does not prevent the installation of the Debug.plist, but there will be no way to get to it, since it's not referenced as a child from the root plist. If this bothers you, you'll probably have to set up another script to remove Debug.plist.
  • What if I'm not using a Settings bundle in my Release builds? This technique assumes that you're already using a Settings bundle to your application, and you want to add extra stuff to it based on build configuration. If you'd like to only have Settings bundle for Debug builds, this isn't going to work for you, though hopefully some of the information can help you get started on a similar solution.

Terminal Quickie: trash

I have been keeping myself quite busy with some other projects, but fear not, the Learning the Terminal series is not dead. Hopefully this will hold you over until I have some time to generate some real content.

Last time, we talked about Terminal and Finder integration. One topic we did not touch upon is how to move files to the Trash from the shell. Perhaps surprisingly, Mac OS does not have a built-in command for trashing files. Sure you can just mv files to the Trash folder directly, but this will not handle name collisions or alternate volumes correctly.

There have been a few attempts at a shell script to mimic the trash behavior, but the best I’ve come across is Dave Dribin’s osx-trash. This version is actually a small ruby program that uses the 10.5 scripting bridge to actually execute the Trash operation through Finder. It works “The Right Way”.

Please see Dave Dribin’s setup and installation directions for more information.

Learning the Terminal on the Mac – Part 4 – Bringing Finder and Terminal Together

The first few articles laid out a good foundation of basic shell usage, but little Mac-specific information. In this article, all that will change. We’re going to show a few ways to get back and forth between Terminal and Finder.

From Finder to Terminal

There a few methods to get information from Finder into the Terminal, or use Finder in Terminal-like ways.

Path Bar

One new feature in the 10.5 Finder is Path Bar. The Path Bar makes it easy to see the full path to the current folder. To enable the Path Bar (if you haven’t already), activate Finder, and choose View → Show Path Bar from the menu.

lt2-show-path-bar.png

Once enabled, the Path Bar will appear at the bottom of all your Finder windows, just above the Status Bar. Below is how the Path Bar appears when I have my Home folder open in Finder.

lt2-path-bar.png

The the above path would be interpreted as /Users/demo in the shell.

lt4-ls-path-bar.png

Go to Folder…

Finder also has a built in command called Go to Folder… that allows a path to be entered in a similar way as in the shell. This feature can be accessed by choosing Go → Go to Folder… from the menu, but it’s faster to use the shortcut ⌘⇧G.

lt2-go-to-folder.png

The path entry field of the Go to Folder… feature also has rudimentary tab-completion support. If you type a few letters and either press the tab key (⇥) or just wait a few seconds, it will complete the current path component with the first match it finds. It’s not nearly as good as the completion in bash, but it’s there.

Drag & Drop Paths

Files or folders in Finder can also be dragged and dropped directly onto Terminal. Doing so will insert the path to the file or folder into the shell.

lt4-drag-drop-path1.png

And voilà, the path is inserted into the shell:

lt4-drag-drop-path2.png

OpenTerminalHere

OpenTerminalHere is a small Applescript application that launches Terminal and automatically cd’s to the current folder in Finder. It was originally written by Marc Liyanage and has since been updated to make it more Leopard-friendly. I find it extremely handy, and if you think you would too, go here for the download and setup instructions.

OpenTerminalHere is meant to be installed into Finder’s toolbar. This is done by simply dragging the OpenTerminalHere icon to Finder’s toolbar and pausing a few seconds until a small green “plus” indicator appears next to the mouse pointer. If you want to tweak the position of OpenTerminalHere or add spacing, choose View → Customize Toolbar… in Finder’s menu after installing.

lt4-install-openterminalhere.png

Now just click on the OpenTerminalHere icon in the Finder window’s toolbar, and a Terminal will launch and cd to the current folder.

From Terminal to Finder

Even though the shell is a powerful tool, sometimes you may want to switch back to Finder to finish a task. Here are a couple tools to do just that.

open

We’ve used the open command (man page) a few times already to open files with their default application and folders in Finder. This command is actually specific to Mac OS and is not found in other Unix-like operating systems. The open command also allows you to choose the application with which to open the file, or open the file in the default text editor.

To open a file using a specific application, use the -a option:

open -a [application] [file]

For example, if I wanted to open a file called “hello.txt” in MacVim instead of the default editor, TextEdit, I would type:

open -a /Applications/MacVim.app hello.txt

Also as we mentioned just sentences ago, you can use the open command to open a file using the default text editor. This is done with the -t option.

open -t [file]

Let’s say I’m a curious individual, and I want to see exactly what iTunes stores in its XML library file. I would type:

open -t ~/Music/iTunes/iTunes\ Music\ Library.xml

(Remember those extra \’s are to escape the spaces, which was discussed in the second article.

Revealing Files in Finder

The open command can be used to open folders in Finder, but we don’t have a way to reveal a file within a folder. Many applications have a built-in “Show in Finder” feature, but unfortunately the shell does not. However, all hope is not lost.

One of the simplest ways to add new commands in the shell is through creating shell scripts. Shell scripting is a deep topic in itself, but for know, just think of a shell script as a small program that is interpreted by the shell itself. Shell scripts are written in plain text so they are quick to create and easy to modify.

A hint entitled “Select files in Finder from Terminal” on Mac OS X Hints presents a way add a “Show in Finder” feature to the shell using a custom shell script. Several others submitted improvements and alternative approaches in the comments. The solution I liked best is described in this comment by caesurae.

I liked this solution because it was relatively concise and easy to read (for the bash-literate), and it is contained within a single file. I modified the script very slightly to fix a problem with revealing directories.

Here is the full script. Immediately following is a download link, which is probably more useful. Following that, and even more useful still are installation instructions.

The Script

#!/bin/bash
#
# revealInFinder.bash
#
# 05.26.2006
#
# caesurae@gmail.com
#
# tested on Mac OS X 10.3.9 build 7W98 - Darwin 7.9.0 - AppleScript 1.9.3
#
# reveal the given file(s) and/or folder(s) in a Finder window
#
# usage: revealInFinder.bash ~/dir/file "/dir/dir/my file" file
#
########
#
# 08.07.2008
#
# Andy Mroczkowski
#
# minor update to allow for more reliable opening of directories
#
# tested on Mac OS X 10.5.4 - AppleScript 2.0.1k
#
##

# if no arguments are given, echo the usage string
if [ $# -lt 1 ]; then
  echo 'Usage: '`basename "$0"`' [files]' >&2
  exit 1
fi

# define $n, gets +1 for each argument given
# not sure if $n is needed, how to get index number of $1, $2, etc.?
n=0

# define $act, gets +1 for each argument that passes test
act=0

# step thru each argument one at a time
for thearg in "$@"; do

  n=$(( n + 1 ))

  # if $thearg exists then
  if [ -e "$thearg" ]; then

    # get the absolute path to the argument
    thearg="`cd \`dirname \"$thearg\"\`; pwd`/`basename \"$thearg\"`"

    # create a applescript statement using $thearg for osascript to execute
    osatext='tell application "Finder" to reveal POSIX file "'"$thearg"'"'
    /usr/bin/osascript -e "$osatext"

    # $act activates Finder after processing the remaining arguments
    act=$(( act + 1 ))

  # else if $thearg does not exist then
  else

    # if $thearg is not the last argument given then
    if [ $n -lt $# ]; then

      # ask to continue processing the remaining arguments
      echo -n '"'"$thearg"'" does not exist and will be ignored. continue?  (y/n)? ' >&2
      read ans
      case $ans in
        "n" ) exit 1 ;;
        "y" ) continue ;;
      esac

    # else if $thearg is the last arguement then
    else

      # print the "does not exist" string and exit
      echo '"'"$thearg"'" does not exist.' >&2

      # exit status 1 indicates an error occurred
      exit 1
    fi
  fi
done

# if $act is greater than 0 then activate the finder
if [ $act -gt 0 ]; then
  /usr/bin/osascript -e 'tell application "Finder" to activate'
fi

# exit status 0 indicates all is ok
exit 0

Download the reveal script (and unzip it if necessary).

Installation

Once you have reveal downloaded, you need to put it a place where the shell can find it. Executable files are usually kept in a directory called “bin“. To avoid any permissions problems, we’re just going to set up this script for the current user. To create a bin directory for yourself, type:

mkdir ~/bin

Now the reveal script must be moved into the bin directory. If you saved reveal in your Downlaods folder, you would type the following:

mv ~/Downloads/reveal ~/bin

Of course if you saved reveal to some place other than your Downloads folder, you’ll have use the appropriate path.

Finally we need to ensure that reveal is executable. If you downloaded it from the using the link above, the permissions should have been preserved within the zip file, but we’ll reset them just in case. Changing basic permissions from the command line is done with chmod (man page). We’ll go into detail on chmod later. For now just type

chmod +x ~/bin/reveal

Ok, now everything is in place, but there is still one more thing left to do. The shell only looks in a few pre-defined places for programs and although a ~/bin directory is common, it does not search that directory by default. We can tell the shell to look for programs in our ~/bin directory by adding it to our PATH.

The PATH variable controls where the shell looks for programs. You can manually type in the command to add ~/bin to our PATH, but the PATH variable is cleared every time we open a new shell. That’s a hassle. Instead we want it so our PATH is set up for us automatically. To do that, we will add the command to a special file called .bash_profile which is loaded every time a new shell is started.

First, create the file. The file must be in your home directory (~) or the shell won’t find it. If you already have a .bash_profile the following command won’t overwrite it.

touch ~/.bash_profile

Now open the new, empty .bash_profile file in your default text editor:

open -t ~/.bash_profile

Next add the following line exactly as it appears below. Copy and Paste are your friends. If you already have some stuff in your .bash_profile just add it to the end.

export PATH=$PATH:$HOME/bin

Save and close the file. Then close the Terminal you were working in and open a new one (your .bash_profile is only read when the shell starts up).

Congratulations, you now have installed a shell script. Have a beer.

Usage

Our new reveal script is easy to use. You just type reveal followed by one or more paths to files or directories. In fact, if you type reveal with no arguments, it prints a helpful usage statement:

Usage: /Users/demo/bin/reveal ~/dir/file "/dir/dir/my file" file

Of course since we went through all the trouble of putting reveal into our path, we can just type:

reveal ~/dir/file "/dir/dir/my file" file

Here it is in action:

lt4-reveal-in-action.png

Wrap-up

So there we have it – Terminal and Finder peacefully co-existing. What a wonderful world. If you have more suggestions about integrating these fine pieces of software, or a question, feel free to comment. Feedback is always welcome.

I got it goin’ on

And what does “it” refer to in this case? Well, several things:

First I’m heading off Paris and Ghent in a couple hours. I most certainly won’t be blogging, and probably not much twittering either. If anything I might update my Facebook status or throw something on my zany new tumblog.

Of course this means no new Terminal article next week. I’ve been trying to get one out weekly (more or less), but the vacation and the inevitable post-vacation catch up will push #4 back a bit. And speaking of the blog and articles…

I’m well aware that the design and layout here is, well, sucktoast. I’ve been trying to actually generate some content before trying to make it all perty (sic). However, I assure you that an improved design that will address a lot of the current layout and formatting issues is being worked on by the (in)famous 20OG design team, and an update should be coming… soon.

Learning the Terminal on the Mac – Part 3 – Basic File Operations

By now you hopefully have a good understanding about how to reference files with paths and list files using ls, and move around with cd. Now we’re going to go over basic file operations including:

  • Create
  • Copy
  • Move
  • Delete

There is a lot of information to cover this time around, but I want to make one quick note about the notation being used before we begin. Like the man pages, we’ll use an underline to denote a placeholder for an option or argument to a command to describe its general form. For example, the general way to describe how to change to a directory is:

cd path

Here path is just a placeholder. You would replace it with a path to an actual directory.

Creating Files and Directories

There are actually many, many ways to create files on your Mac using commands in the shell, but I’m going to demonstrate a few quick ways so you can easily experiment with copy, move, and delete operations.

touch

The touch (man page) is a simple command that “touches” a file, meaning it updates its last modification date to the current time.

touch file

A nifty feature of touch is that it will create the file it it doesn’t exist. This command is the easiest way to create a new file to play with. For example to create a file called “Bananas!”:

touch Bananas!

And the same command, in the terminal:

lt3-touch.png

echo

The simplest way to create a file that has some contents is with the echo (man page) command, and output redirection. We’ll cover output redirection in detail soon. For now just follow the form:

echo "some text" > filename

An example:

lt3-echo.png

We snuck in another command there too: cat (man page). cat is used for concatenating (hence its name) files, though it’s most often used for dumping the contents of a text file to the screen.

mkdir

Finally, directories can be created with mkdir (man page).

mkdir new directory

To make a directory called “Stuff” in my home directory:

mkdir ~/Stuff

A simple example, creating the same “Stuff” directory, but using a relative path:

lt3-mkdir1.png

Copying: cp

Copying files is done with the cp command (man page). This command takes at least two arguments; the first being the source and the second being the destination.

cp source destination

Copying a file from my Downloads folder to my Desktop looks like this:

computer:~ demo$ cd Downloads/
computer:~ demo$ cp file.dmg ~/Desktop/

A real example:

lt2-cp1.png

The cp command can also be used to copy several files to a single destination:

cp source 1 source 2 ... source n destination

For example:

computer:~ demo$ cd Downloads/
computer:Downloads demo$ cp file1.dmg file2.dmg ../Desktop/

Moving: mv

The mv command (man page) command is used for moving files to a different location, or renaming files. It’s usage is very similar to cp:

mv source destination

mv source 1 source 2 ... source n destination

For example:

computer:~ demo$ cd Downloads/
computer:Downloads demo$ mv file1.dmg file2.dmg ~/.Trash/

Here is a real example of moving a file from my Downloads folder to my Desktop, and then renaming that file:

lt2-mv1.png

Also, mv can be used for moving entire directories. Here’s an example that shows moving a folder called “Stuff” from the Desktop to the home directory:

lt2-mv3.png

Deleting: rm

Files and directories can be deleted (or removed) with the rm command (man page). A word of caution: rm will remove files immediately, skipping the Trash Can. Use it carefully.

rm file 1 file 2 ... file n

The following is an example using touch to create several files and rm to delete them:

lt3-rm1.png

Recursion

For more information see recursion.

Ok, we got that lame computer science joke out of the way. Recursion isn’t all fun and games, though. It is an option that is found in many shell commands, including some of the ones we just learned! In this context, recursion basically means to include all files including sub-directories, sub-sub-directories, etc. Both cp and rm can be instructed to act recursively with the -r option.

cp -r

With the -r option, cp can be used to copy a directory and all its contents to another location.

cp -r source dir destination

For example, if I wanted to copy my “Documents” folder to a “Backup” volume:

cp -r Documents /Volumes/Backup/

Note that there is no trailing / after the source directory, Documents. This is an important subtlety when using cp -r. If the trailing / is omitted, the directory and all its contents will be copied. However, if the trailing / is included, only the contents of the directory will be copied to the destination, not the directory itself. Here are two examples showing the differences between the two commands:

With a trailing /, only the contents will be copied:

lt3-cp-r2-annotated.png

And now without the trailing /, the entire directory will be copied:

lt3-cp-r3-annotated.png

rm -r

If you thought rm was dangerous before, you ain’t seen nuttin’ yet. Just like with cp, the -r option allows you to remove a directory and all its contents. It is typically paired with the force option, -f, which swallows certain errors and skips all prompts and warnings.

rm -rf stuff you never want to see again

Example:

lt3-rm-rf.png

Be especially careful with -f. There is no undo.

Wildcards

If you haven’t guessed already, bash supports basic wildcards for matching files and paths. The real term for this functionality is globbing, which we’ll cover in more detail in the future. For now, we’ll cover two special characters which you’re probably already familiar with from other pieces of software: * and ?.

Match many characters:*

The asterisk (*), or sometimes called “star”, is the standard “match everything” wildcard. For example, say you want to copy all DMGs from the Downloads directory to /Volumes/Backup, but not the other files.

computer:~ demo$ cd Downloads/
computer:Downloads demo$ cp *.dmg /Volumes/Backup

This would match any file (or directory) that ends with .dmg. However, some DMGs are also zipped and end with .gz or .zip. We can make sure we get all the DMGs but using two asterisks:

computer:Downloads demo$ cp *.dmg* /Volumes/Backup

Most commands that take a path as an argument, including mv and rm will also work with . But again, be very careful with rm and .

Here is how you could clean out your ~/Library/Caches folder by moving al its contents to the Trash. If you actually do this, you should reboot after the operation is complete.

lt3-mv-caches-to-trash.png

Match one character: ?

The ? wildcard isn’t used as often as but it is still fairly common. It differs from in that it only matches one character. It’s similar to the blank tile in Scrabble. If you many digital photos downloaded from your camera, and they all have names like IMG0020.jpg and IMG0164.jpg, you could move them all like this:

computer:New Photos demo$ mv IMG_????.jpg ~/Pictures

The ? only matches a single character, so to match a four-digit number, we need to place four ?’s in a row.

Files and Directories Revisited

You may have noticed that the same commands can be used to copy, move, and delete files and directories (though not create, sorry).

This is because UNIX-based operating systems treat directories as a special kind of file. This means that many of the commands used for manipulating files can also be used for manipulating directories. UNIX actually goes beyond just directories. It also handles things like devices and network sockets as files as well, but that is a bit beyond the scope of our current article.

Wrap-up

The information covered in these last three tutorials should give you a very powerful toolbox. You can now do many of the basic file management tasks you would normally do in the Finder in the shell instead. This article didn’t discuss much Mac-specific information, but the tentative topic for the next article is how to bring the Finder and the Terminal together.

As always, I appreciate your feedback about the current material and future directions. What would you like to see next?