Configuring bash with aliases and functions

In the previous posts we talked about which files you could use to customize your bash environment.

There are (mainly) three customizations you can perform:

This post will look at some useful aliases and functions.

bash aliases

Note: bash aliases are something completely different from Finder aliases. The closest shell equivalent for Finder alias files are symbolic links.

bash aliases are basically text substitutions. For example a common alias is to define:

alias ll="ls -l"

You can type this alias definition directly into bash but then the definition will only exist for that particular shell. When you open a new terminal window it will know nothing of this alias. If you want an alias to exist in all your shells (and usually you do), you need to add the above line to your .bash_profile or .bashrc. (Learn about the difference here.)

Whenever you modify the .bash_profile or .bashrc it will not automatically be loaded into any shell that is already open, you either have to close the Terminal window and open a new one or write

$ source ~/.bash_profile

(or ~/.bashrc) to load the new settings.

Once you have set the alias, anytime you type ll at the start of a command, bash will replace it with ls -l before executing. Since subsequent arguments are left alone they will just be picked up by the substituted ls command, so if you type ll -a bash will substitute that to ls -l -a and it will work just your would expect.

You can make the alias more complex if you want:

alias lll="ls -alhTOe@"

If you always want to use the long format of ls, you can alias the ls command itself:

alias ls="ls -l"

Then, when ever you type ls it will be replaced with ls -l. Note the lack of spaces around the ‘=’, as usual when assigning values in bash.

Uncovering the alias

You can unset or delete an alias with the unalias command:

$ unalias ls

This will return to the default ls command (for this bash instance only).

Since alias substitution only happens at the beginning of the command, you can also bypass it once by starting the command line with a different character:

$ "ls" -S
$ \ls -S

Either way of typing this will use the original ls command, avoiding substitution, just this once.

If you are unsure if a command has an alias or want to know what gets substituted, then you can have the alias definition printed with

$ alias ls
alias ls='ls -l'
$ alias lll
alias lll='ls -alhTOe@'

You can also list all defined aliases by running alias without any arguments:

$ alias
alias ll='ls -l'
alias lll='ls -alhTOe@'
alias ls='ls -l'

Some Alias examples

Some users like to alias the potentially dangerous commands rm, mv and cp with the -i option, which forces the user to confirm when a file is going to be deleted or overwritten:

alias rm="rm -i"
alias mv="mv -i"
alias cp="cp -i"

Then if you do something that could destroy an existing file you will be prompted to confirm:

$ rm my_important_file 
remove my_important_file? 

You can still use \rm to bypass the alias, if you believe you know what you are doing and want to avoid being prompted several times.

You can add short cuts to cd to parent folders:

alias ..="cd .."
alias ...="cd ../.."
alias cd..="cd .."

Since alias substitution only takes place at the beginning of the command, you can still use .. normally as an argument.

Note that the last alias cd.. is a substitution of a common typo. Since the output of the alias is not checked for further alias substitutions you cannot use alias cd..=.. to define it using the previous alias. Each alias must stand for itself.

You can also use aliases to make complex or hard to memorize commands easier to remember:

alias badge="tput bel"

The first command will play a system alert and set a ‘badge’ on the terminal application if it is in the background. This is useful to get notified of long running commands:

$ hdiutil convert image.sparseimage -format UDZO image.dmg; badge;

The substitution can be long and multiple commands can be separated by ;

alias dockspace="defaults write persistent-apps -array-add '{\"tile-type\"=\"spacer-tile\";}'; killall Dock;"

The dockspace alias uses this hack to add a spacer item to the dock.

We have seen earlier with the ll alias that any text or arguments after the alias will just be added to the substituted command. We can use this to our advantage:

alias reveal='open -R'
alias xcode='open -a Xcode'
alias pacifist='open -a Pacifist'

All of these commands will require a file path as an ‘argument’ to work properly.

Functions: beyond the alias

While bash aliases are useful and easy to define, they have limitations. Aliases will only be replaced at the beginning of the command prompt. Also additional arguments will be appended after the replacement. If you need to insert additional arguments somewhere in the replacement, you cannot achieve that with aliases.

For example, to display the man page for ls in the Preview application you need the following commands:

$ man -t ls | open -f -a "Preview"

If you wanted to alias this you would have to insert the arguments after man -t and before the pipe. Since aliases, only substitute the beginning of the command, this will not work. we need to define a bash function:

function preman() {
    man -t $@ | open -f -a "Preview"

Sometimes you will see bash functions defined without the function keyword, but I prefer to use it since it makes the code more readable.

The $@ will be replaced with all the arguments passed into the function, so when use the function

$ preman ls

the argument ls will be substituted where the $@ is.

You can also use the positional argument variables $1, $2, etc:

function recipe-open() {
    open "$(autopkg info '$1' | grep 'Recipe file path' | cut -c 22-)"

Within functions the full power of bash scripting is at your service. You can even call other scripting languages.

# prints the path of the front Finder window. Desktop if no window open
function pwdf () {
    osascript <<EOS
        tell application "Finder"
            if (count of Finder windows) is 0 then
                set dir to (desktop as alias)
                set dir to ((target of Finder window 1) as alias)
            end if
            return POSIX path of dir
        end tell

# changes directory to frontmost 
alias cdf='pwdf; cd "$(pwdf)"'

The last alias cdf has to be defined as an alias. Since a function or script could not change the directory for the current shell, you have to use alias substitution to get the cd.

In general functions (and scripts) are more powerful and versatile than aliases, but aliases provide and easy and comparatively safe way to customize the your shell environment.

Putting it all together

Here is a sample .bash_profile with many of the examples given here and in the previous article. You can use this as a starting point for your own bash configuration.

On bash Environment Variables

This is vacation time with a holiday in the Netherlands. Posting will be more sporadic than usual.

In the previous post we talked about which files you could use to customize your bash environment.

There are (mainly) three customizations you can perform:

This post will look at some useful environment variables.

Choose your own PATH

The PATH environment variable contains a list of directories that bash will search through for commands. You can show your current path by echoing the PATH variable:

$ echo $PATH

This is the default PATH for a ‘clean’ macOS. Depending on what you have installed, there may be more elements in your PATH. The individual directories are separated by a colon :. When you enter a command, like for example systemsetup at the prompt, bash will search through the directories in PATH in the order until it finds a match.

Note: if there is ever any question on which command is actually executed, you can use the which command to get the effective path to a given command from the shell:

$ which python
$ which systemsetup
$ which bash

You might have installed other (newer versions) of certain tools (such as python or bash itself). Depending on how you install them they may be installed in a way that overrides the tool that is part of macOS. This may lead to problems when scripts assume the system version of a tool.

You can add your own directories by either appending (safe) or prepending (risky) your own directories to the PATH variable:

export PATH

This will add the ~/bin directory (which does not exist by default, you have to create and fill your own) to your PATH. The export command tells bash it should export this variable to child processes.

You can add more than one directory to the PATH, either at once, or one per line:

export PATH=$PATH:~/bin:~/Projects/scripts


export PATH

Since bash stops looking when it finds a match, the order of the directories in the PATH is important. When you place your directories before the default directories you can override some of the system commands. This can be powerful, but also very dangerous. There are safer methods for overriding system commands. You should add your directory to the end of the PATH to be safe.

Note: the PATH (and other environment variables) are exported to child-shells and scripts executed from the shell. However, not every tool executes directly from a shell, or from a shell with your configuration file. So you should never assume PATH (or any other environment variable) is set to anything specific (or even set) when you write a script. You can either set the PATH at the beginning of a script, or use absolute paths for commands in scripts such as /usr/bin/defaults or /usr/bin/pkgbuild. You can determine the path to a command with the which command.

Prompt Me!

You can also configure the command prompt, i.e. the text that bash displays before you enter a command. By default the macOS prompt looks like this:

hostname:currentDir user$ 

The configuration for the prompt is stored in the PS1 environment variable. You can see the default value by echoing it:

$ echo $PS1
\h:\W \u\$

The letters with the backslash \ in the prompt variable are placeholders that will be replaced with a specific value when the prompt is printed out.

  • \h will be replaced with the hostname of the computer up to the first . (\H would be the complete hostname)
  • \W will be the basename of the current working directory
  • \u is the current user
  • \$ will be $ unless the current user is root or effectively elevated to root privileges, in which case it will be #. This is very useful as a warning when working with elevated privileges.

Since I am the only user on my Mac, and I also know which Mac I am sitting at, I have changed my prompt to something simpler:

export PS1="\W \$ "

Note the space after the \$. If you forget that the cursor will stick right on the $.

(When I log in to a different Mac with ssh the different prompt also reminds me that this shell is on a different Mac.)

You can find a list of escape sequences here.

You can also add color to the prompt data, but the escape sequences will look very messy very quickly. To have the directory name in gray, you use:

export PS1="\[\e[1;30m\]\W\[\e[m\] \\$ "

List in Color

You can also tell bash to color for some other commands, such as ls. To do that you just have to set the CLICOLOR variable:

export CLICOLOR=1

Change the Editor

Some terminal commands will open a text editor directly. For example git may open a text editor so you can enter a commit message. By default the system will open vi but you can change the editor that will be used. There are even some graphical editors that can be used this way. The environment variable for this behavior is EDITOR.

vi or vim:

export EDITOR=vi
export EDITOR=vim


export  EDITOR=emacs

nano or pico:

export EDITOR=nano
export EDITOR=pico


export EDITOR="open -f"


export EDITOR="bbedit -w --resume"


export EDITOR="mate -w"


export EDITOR="subl -n -w"


export EDITOR="atom --wait"

Note: the -w or --wait argument tells the UI application that there is another process waiting for their output.


Putting all of this together, you can add these lines to your `.bash_profile` or `.bashrc`:

Up Next?

In the next post we will look at bash aliases and functions.

About bash_profile and bashrc on macOS

Note: bash_profile is completely different from configuration profiles. Learn more about Configuration Profiles in my book: ‘Property Lists, Preferences and Profiles for Apple Administrators’

Note: please consider supporting the author of this site by purchasing one of my books! Thank you!

In this spontaneous series on the macOS Terminal I have often mentioned adding something as an alias or function to your bash_profile or bashrc. Obviously, you may wonder: how do I do that? And which file should I use?


When you work with the command line and the bash shell frequently, you will want to customize the environment. This can mean changing environment variables, such as where the shell looks for commands or how the prompt looks, or adding customized commands.

For example, macOS sets the PATH environment variable to /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin by default. This is a list of directories (separated by a colon ‘:’) that the system searches through in order for commands. I like to add a folder in my home directory ~/bin to that list, so that I can execute certain tools without needing to type out the full path. (e.g. munkipkg, quickpkg and ssh-installer).

In bash you append to existing PATH do this with:

export PATH="$PATH:~/bin"

You could type this command every time you open a new Terminal window (i.e. shell), or you can configure your shell to do this automatically.

Depending on which shell you use and how you start the shell, then certain script files will be executed which allow you to set up these customizations.

This article will talk about customizing bash on macOS. Other shells and other operating systems may have other files or rules.

So, which file?

Thanks to the rich and long history of bash the answer to which file you should put your configuration in, is surprisingly confusing.

There are (mainly) two user level files which bash may run when a bash shell starts. ~/.bash_profile or ~/.bashrc.

Both these files are on the first level of your home directory ~/. Since the file names start with a . Finder and normal ls will not show them. You need to use ls -a to see if they are present. Read more about invisible and hidden files here.

The usual convention is that .bash_profile will be executed at login shells, i.e. interactive shells where you login with your user name and password at the beginning. When you ssh into a remote host, it will ask you for user name and password (or some other authentication) to log in, so it is a login shell.

When you open a terminal application, it does not ask for login. You will just get a command prompt. In other versions of Unix or Linux, this will not run the .bash_profile but a different file .bashrc. The underlying idea is that the .bash_profile should be run only once when you login, and the .bashrc for every new interactive shell.

However, on macOS, does not follow this convention. When opens a new window, it will run .bash_profile. Not, as users familiar with other Unix systems would expect, .bashrc.

Note: The Xterm application installed as part of Xquartz runs .bashrc when a new window opens, not .bash_profile. Other third-party terminal applications on macOS may follow the precedent set by or not.

This is all very confusing.

There are two main approaches:

  • When you are living mostly or exclusively on macOS and the, you can create a .bash_profile, ignore all the special cases and be happy.
  • If you want to have an approach that is more resilient to other terminal applications and might work (at least partly) across Unix/Linux platforms, put your configuration code in .bashrc and source .bashrc from .bash_profile with the following code in .bash_profile:
if [ -r ~/.bashrc ]; then
   source ~/.bashrc

The if [ -r ... ] tests wether a file exists and is readable and the source command reads and evaluates a file in place. Sometimes you see

[ -r ~/.bashrc ] && . ~/.bashrc

(mind the spaces) Which is a shorter way to do the same thing.

Since either file can drastically change your environment, you want to restrict access to just you:

$ chmod 700 ~/.bash_profile
$ chmod 700 ~/.bashrc

That was confusing. Is that all?

No. There are more files which may be executed when a shell is created.

When bash cannot find .bash_profile it will look for .bash_login and if that does not exist either .profile. If .bash_profile is present the succeeding files will be ignored. (though you can source them in your .bash_profile)

There is also a file /etc/profile that is run for interactive login shells (and This provides a central location to configure the shells for all users on a system. On macOS /etc/profilesets the default PATH with the path_helper tool and then sources /etc/bashrc which (you guessed) would be the central file for all users that is executed for non-login interactive shells. For macOS /etc/bashrc sets the default prompt and then itself sources /etc/bashrc_Apple_Terminal which sets up the session persistence across logins.

So in macOS, before you even see a prompt, these scripts will be run:

  • /etc/profile
    • /etc/bashrc
      • /etc/bashrc_Apple_Terminal
  • if it exists: ~/.bash_profile
    • when ~/.bash_profile does not exists, ~/.bash_login
    • when neither ~/.bash_profile nor ~/.bash_login exist, ~/.profile
  • ~/bash_profile can optionally source ~/.bashrc

There is also a file ~/.inputrc, where you can setup certain command line input options. One common example for this is to enable case-insensitive tab-completion. You can find a list of more options here.

Finally, there is ~/.bash_logout which is run when a shell exits or closes.

Ok, so I have the file, now what?

Whichever file you choose, (I went with option one and have everything in .bash_profile) now you want to put stuff in it.

Technically this is a script, so you can do anything you can code in bash. However, usually the contents of a .bash_profile or .bashrc fall into one of three categories:

I will show some examples for each in the next post!

Terminal Keys and Commands Reference

There have been many posts on Terminal on macOS recently. To catch up, here is a list of all the posts, so far:

And here are a few older posts that also fit well in this topic:

I have a few more ideas and will continue this series as long as I can think of topics. If you have suggestions, please let me know on Twitter, the MacAdmins Slack or in the comments!

And for reading through all of this you get a reward! Download a two-page PDF with all the most important Terminal Keys and Commands! Print it and put them up next to your screen!

You can also download another two-page PDF reference for my book ‘Property Lists, Preferences and Profiles for Apple Administrators’.

Navigating the Terminal Prompt

I have talked a lot about the Terminal recently. One the most basic elements of a command shell is the prompt line, where you enter the command. Sometimes you have to move the cursor around the prompt line and there are more efficient ways of doing this than hitting the left arrow multiple times.

These key commands are for the default Terminal configuration with the bash shell.

Recalling previous commands

Often you want to retry or re-use a previous command. Hitting the up arrow will recall the previous command, leaving the cursor at the end of the line, so you can either hit return to execute the command or edit it. You can hit the up arrow multiple times to go back further in your command history. You can also hit the down arrow to move forward again.

Instead of hitting the up arrow several times, you can also hit ctrl-R and start typing a command you used before. This will search through the history backwards and recall the latest command you used starting with what you typed. So ctrl-R and then typing cd will recall the last cd command you typed. When you get ‘stuck’ in the search mode you can leave it with ctrl-G.

Moving the cursor

Once you have recalled (or typed) a command and want to edit it, you will have to move the cursor.

The left and right arrows move the cursor left and right. You can use (option) ⌥-left (or right) to move word by word. You can use ctrl-A to jump to the beginning of the line and ctrl-E to jump to the end.

ctrl-XX (hold ctrl and press ‘x’ twice) will jump to the beginning of the line and then back to the current position the second time you use it.

If you recalled a wrong command, then you can clear the entire part left of the cursor with ctrl-U and the part right of the cursor with ctrl-K. There does not seem to be a keystroke that just clears the entire current line, but most of the time your cursor will be at the end of the line, so ctrl-U will do the trick.

And finally, you can option-click with the mouse pointer on a character in the command line to move the cursor there.

Selecting text

When you hold the option key, the mouse pointer will turn from the text insertion pointer to a cross. This is because the option key also switches text selection to rectangular. This can be useful when copying text from the table like output like ls -l or df.

As in any Mac application, you can double-click to select a word in the Terminal. However, for macOS a ‘word’ ends at special characters, including the /. This is annoying when you want to select a file path. If you double-click while holding command-shift Terminal will select the entire file path. This works for URLs, too.

Usually, you select some text to copy and paste it into the command prompt. You can take a short cut here, too. Terminal has a ‘Paste Selection’ command in the ‘Edit’ menu (command-shift-V). This way you save the ‘copy’ step.

And (as a recap from the post on marks) command-shift-A will select the output of the previous command.

Tab completes me

When you are typing commands, file names or paths, then you can use the tab key ⇥ to save keystrokes and typos.

For example you can type cd ~/Doc⇥ and it will complete to cd ~/Documents/. You can hit tab over and over at different parts of the command:

$ cd ~/Li⇥
$ cd ~/Library/Appl⇥
$ cd ~/Library/Application\ Su⇥
$ cd ~/Library/Application\ Support/

When there are multiple options to complete, bash will play an alarm sound, if you then press tab for the second time, it will list all options:

$ cd ~/D⇥<beep>⇥
Desktop/   Documents/ Downloads/

Using tab-completion not only saves keystrokes, and time, but also reduces the potential for typos and errors. Tab-completion will also automatically escape problematic characters such as spaces:

$ cd /Library/Appl⇥
$ cd /Library/Application\ Support/

Note: usually bash is case-sensitive. However, since the macOS filesystem (HFS+) is (for now) case-insensitive, you may want to switch tab-completion to be case-insensitive, too. The case-sensitivity of the file system may change with the upcoming APFS file system.

There is another form of completion which you invoke with pressing the esc(ape) key twice. This will replace bash ‘globbing’ characters such as ?, * and ~. For example:

$ cd ~/Doc*[esc][esc]
$ cd /Users/armin/Documents

Note that this replaces the ~ and the *. When there are multiple possible expansions, there will be an alert sound. You can press esc twice again to see all possible expansions:

$ cd ~/D*[esc][esc]<beep>[esc][esc]
Desktop/   Documents/ Downloads/

Terminal: The ‘[‘ Marks the Spot

Using Terminal on macOS you may at some point have wondered about the small gray square brackets before the prompt. (There is also a counterpart closing bracket ‘]’ at the right side of Terminal window.)

Marks in Terminal

They appeared first in Terminal in El Capitan (10.11). They are called ‘Marks’ and simplify scrolling through the output in the Terminal window.

By default, every command that is executed automatically gets marked.

Jump to the Mark

You can quickly scroll the Terminal output to previous marks (i.e. command prompts) with ⌘↑ (Command-Up Arrow) and to next mark with ⌘↓ (Command Down-Arrow). This allow you to quickly skip through the command prompts in your window.

Select Output

You can quickly select output between Marks. ⌘⇧↑ (Command-Shift-Up) will select the output up to, but not including the previous command prompt. ⌘⇧↓ (Command-Shift-Down) will select the output up to, but not including the next command prompt. When you repeatedly hit this key combo the selection will be extended further, including the intermittent command prompts.

⌘⇧A (Command-Shift-A) or ‘Select Between Marks’ from the ‘Edit’ menu, will select to the previous mark, if you are the last (empty) command prompt, otherwise it will select to the next mark.

Clearing Output

You can also just remove the output of the previous command with ‘Clear to Previous Mark’ from the ‘Edit’ menu (⌘L). This is less destructive than clearing the entire screen or buffer.

Marks and Bookmarks

Marks are very useful. However, they are all the same, so jumping back to a specific mark in your Terminal scroll buffer will get harder and harder you have to hit ⌘↑ more and more often.

You can elevate a mark to a bookmark, which will allow you to jump directly to it. Jump to the line you want to bookmark or select some text and choose ‘Mark as Bookmark’ from the ‘Edit’ menu (⌘⌥U) or context menu.

Bookmarks have a bolder vertical bar ‘|’ at the left and right edge of the window.

You can jump to previous and next bookmarks with ⌘⌥↑ and ⌘⌥↓, or you jump directly to a bookmark from the list in the ‘Edit’ > ‘Bookmark’ menu.

The ‘Edit’ > ‘Bookmarks’ menu also has ‘Insert Bookmark’ and ‘Insert Bookmark with Name…’ items. These will mark the current command prompt as a bookmark, and give you the option of naming the bookmark, rather than having the default timestamp as the name.

Terminal will set an automatic bookmark when restoring a Terminal window (after a reboot). This setting is controlled in the ‘Resume’ area in the ‘Window’ Tab of the Profiles area of the settings.

Not Leaving Marks

If the grey brackets indicating the marks annoy you, you can hide them with ‘Hide Marks’ from the ‘View’ menu. This will only hide the marks and bookmarks, they will still be automatically (or manually) created and you can still jump to them.

You can disable automatic mark creation entirely by deselecting ‘Automatically Mark Prompt Lines’ from the ‘Edit’ > ‘Marks’ submenu. When you have disabled automatic marking, you can execute and manually mark a prompt with ⌘↩︎ (Command-Return).

With automatic marking enabled, you can execute a command without marking with ⌘⇧↩︎ (Command-Shift-Return).

You can also manually remove a mark, by scrolling or jumping to it and choosing ‘Unmark’ from the ‘Edit’ > ‘Marks’ menu or the context menu.

Marking Spots Programatically

I have added the following aliases to my .bash_profile:

alias mark="osascript -e 'if app \"Terminal\" is frontmost then tell app \"System Events\" to keystroke \"u\" using command down'"
alias bookmark="osascript -e 'if app \"Terminal\" is frontmost then tell app \"System Events\" to keystroke \"M\" using command down'"

This way I can put the mark in between two commands and just let them run:

$ system_profiler SPApplicationsDataType; mark; system_profiler SPExtensionsDataType

The downside of this approach that the aliases do not work when Terminal is in the background.

PR3 Book Launch Gift! — Command Line Reference

‘Property Lists, Preferences and Profiles for Apple Adminstrators’ launches today! Go get it before the iBooks Store runs out of bytes!

You can read more about the book in my pre-sale announcement post.

While writing the book. I built a list of common and important commands for the ‘Property Lists, Preferences and Profiles for Apple Administrators’ book. (Thanks to François for the idea!)

The list is included in the book resources as a PDF, so you can print it and hang it above your desk. However, you can also download it directly here.

(If you are a consultant, trainer or other service provider of some sorts, your customers might enjoy these, too. There is even white space on the second page for your contact and logo.)


And when you finish the book, please leave a review on the iBooks Store!

Terminal and the Clipboard

Continuing in my informal series of Terminal articles, I’d like to visit two tools that help interact from the shell to a particular part of the macOS UI: the clipboard.

The clipboard is the ‘place’ where macOS stores anything you cut (⌘X) or copy (⌘C). Later the system reads from the clipboard (and possibly converts the data) when you paste (⌘V).

Terminal can trivially interact with the clipboard. You can select text in the Terminal and copy it, and then paste it elsewhere, you can also paste text in the command prompt itself.

(This is useful when you read a great iBook with many Terminal commands. You can simply copy the command from the iBook text and paste into Terminal.)

There are however, two commands, specific to macOS that connect the clipboard closer to other shell commands.

pbcopy will take the contents of stdin (usually text, but could be any stream of data) and put them in the clipboard. So, anything you pipe into pbcopy will end up in the clipboard, so you can paste it into a different application.

(NeXTSTEP was the operating system that Mac OS X was originally based on. What was called the ‘clipboard’ in Mac OS was called ‘pasteboard’ in NeXTSTEP. While all references visible to the user where changed to the Mac OS naming, you still find the old NeXTSTEP names in the ‘innards’ of macOS, hence pbcopy and pbpaste.)


# easier than open, select all, copy
$ cat ideas.txt | pbcopy
# converts Word Doc to plain text and puts it in the clipboard
$ textutil -convert txt MyBook.docx -stdout | pbcopy
$ ipconfig getifaddr en0 | pbcopy
$ system_profiler SPHardwareDataType SPSoftwareDataType | pbcopy
$ uuidgen | pbcopy

I use the last command a lot when I need UUIDs for custom configuration profiles. It is much easier to pipe the output directly into the clipboard, than to select and copy the output.

One downside of this it, that you cannot see what is piped into the clipboard. You can easily make the new clipboard contents visible by typing pbpaste as the next command:

$ uuidgen | pbcopy
$ pbpaste


$ uuidgen | pbcopy; pbpaste

(Several UUIDs were wasted writing this article.)

pbpaste is the counterpart to pbcopy. pbpaste will take the contents of the clipboard, and if they are plain text write them to stdout. That alone can be useful when you want to visualize the clipboard but gets more powerful when you pipe or substitute it into other unix commands.


# after copying the output of uuidgen
$ plutil -replace PayloadUUID -string $(pbpaste) MyConfigProfile.mobileconfig
# copy an html snippet from somewhere or
$ echo '<a href="">Scripting OS X</a>' | pbcopy
# then
$ pbpaste | textutil -stdin -format html -convert rtf -stdout | pbcopy

This last command will convert an HTML snippet into rich text (rtf).

On Bash History Substitution

There are some things in every profession or area of expertise that seem so obvious to you and most people around you. However, there was a point in your career where you did not know this yet and somebody taught you.

A brief discussion on the MacAdmins Slack yesterday led to such a moment. A bash shortcut that seems so obvious to me and probably to many who read this, but again, not everyone knows. And it was someone’s lucky ten thousand moment to learn an wonderful shell shortcut:

sudo !!

Re-visit your (shell) History

The bash shell remembers a certain number of commands that you entered. This way you can ‘recall’ them with the up arrow or the ctrl-R keystroke.1

There are also history substitutions to save typing. And not only do they save typing but these automations, like tab-completion will make you type less errors.

Sudo Make me a Sandwich2

So imagine you typed

$ profiles -P
profiles: this command requires root privileges

and the system helpfully reminds you that you need root privileges to run this. You could retype the entire command after sudo. You could also hit up-arrow, ctrl-a and type sudo[space][return], which is some improvement.

Or you just type

$ sudo !!
sudo profiles -P
There are no configuration profiles installed

The shell will substitute the !! (‘bangbang’) with the previous command. The shell will print the entire command after the substitution (this can be really helpful when things don’t work as expected) and then immediately execute the full command.3

Personally, I only ever use this with sudo. I could imagine there are other uses, maybe with ssh user@host -c.

You can also just type !! to repeat the last command, I find ‘up-arrow, return’ to be more intuitive (and one keystroke less).

Historical Arguments

The other history substitution I use is more specific, but still has its uses. The shell substitutes !$ with the last used argument. The catch here is that if the previous command had multiple arguments, !$ will be the very last argument on that line, not all the arguments.

Some examples:

$ mkdir -p MyNewInstaller/payload
$ cd !$
cd MyNewInstaller/payload
$ touch postinstall
$ chmod +x !$
chmod +x postinstall
$ pkgbuild --component /Volumes/Firefox/ --install-location /Applications Firefox-51.0.1.pkg
$ cp !$ /Volumes/SoftwareArchive/Mozilla/
cp Firefox-51.0.1.pkg /Volumes/SoftwareArchive/Mozilla/

(Most pkgbuild examples are easily dated with the Firefox version.)

If the last command had no arguments, !$ will be the ‘zeroth’ argument, the command itself:

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32
$ !$
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32

More History

There are more bash history substitutions. I found a helpful StackOverflow post with examples here. Most are a bit too cumbersome to actually use the frequently.

Though, I myself had a ‘lucky ten thousand’ moment, since I did not know about !* (all the previous command’s arguments) and !^ (the previous command’s first argument) until today.

Path Through History

This is not directly related to the shell history, but `cd` also remembers your previous location in the file system and you can quickly jump back with `cd -`.

$ cd ~/Projects
$ cd ~/Library/AutoPkg/
$ cd -
$ pwd

  1. hit ctrl-R and type some text, to get the first command in your history containing that text
  2. If you do not recognise that reference, you are one the ten thousand, again.
  3. Yes, technically, sudo !![return] is the same number of keystrokes as up-arrow, ctrl-a and sudo[space][return] but I still find it easier.

The macOS open Command

Most Terminal users will know that

$ open .

will open the current working directory in a Finder window. (You, dear wonderful reader, know this because you read my previous post on Terminal-Finder Interaction.)

However, the open command can do so much more.


Trivially, it cannot merely open the current working directory, but any path:

$ open ~/Library/Preferences
$ open /etc
$ open ../..

This can be used as a quick way to navigate to hidden directories.

You can also open multiple folders at once:

$ open ~/Documents ~/Desktop ~/Downloads
$ open ~/D*

To clean up, you can option-click any close button in a Finder window to close all Finder windows. Or you can use the keyboard short cut ⌘⌥W.


open can also open files. In general you can think of open as the command line equivalent of double-clicking a file or folder in Finder.

$ open document.pdf

will open document.pdf in the current working directory with the default application for PDF files (usually Preview). You can use this against multiple files as well:

$ open ~/Desktop/Screen\ Shot\ *.png

will open all screenshot files (if any) in a viewer in the default application (Preview).


If you have changed the default application that handles a file type or want to override the default application, you can use the -a option:

$ open -a Preview ~/Desktop/Screen\ Shot\ *.png
$ open -a TextEdit web.html

You can specify just the name of an application or the full path, i.e. /Applications/ If you need to be specific, you can also specify an application’s bundle identifier with -b

If you want to open a document but keep the application and the new document window in the background, use the -g option.

$ open -g ~/Desktop/Screen\ Shot\ *.png

Text Editors

There are two interesting special cases for designating applications:

$ open -e helloworld.swift

will open a file with TextEdit.

$ open -t helloworld.swift

will open a file with the default application for text files (.txt file extensions) You can use the Finder Info panel to change the default application or, if you want more fine grained control use RCDefaultApp. In the default macOS config these are the same, but you can of course change the default app to your favourite text editor. (Many text editors, like BBEdit and Atom, have their own CLI tool, but if they don’t, you can use open -t instead.)

You can even pipe text into open with the -f option:

$ ls -l ~ | open -f     # TextEdit, '-e' is implied
$ ls -l ~ | open -tf    # default application assigned to txt

You can set your $EDITOR environment variable: EDITOR='open -tnW'; export EDITOR and then command lines tools that expect text from an editor, like git commit, will get the text from open and thus your default text editor instead. The -n option will actually open a new (sometimes second) instance of the application and the command line tool will resume when you quit this new instance. This a somewhat awkward workflow for Mac users. Many text editors provide a command line tool that may work better in these cases. For BBEdit the correct $EDITOR value is bbedit -w --resume.

Showing Files in Finder

If you are working on a file in Terminal and want to locate it in Finder, open can do better than just opening the enclosing folder. It can select a given file as well:

$ open -R helloworld.swift

Will open a Finder window with the enclosing folder of helloworld.swift and select the file. (You can pass multiple files into open -R but it will only select the last file in the list.)


Finally there is one more useful thing you can open:

$ open   # default browser
$ open vnc://TestMac.local       # Screen Sharing
$ open x-man-page://open         # show man page in Terminal

and, as always, you can use the -a option to override the default application:

$ open -a Firefox

Header files

For the sake of being complete: you can also open header files quickly with open. The -h option will search and open the header file for a given class. There is an additional -s option to choose an SDK:

$ open -h NSTask
$ open -h NSTask -s OSX10.12
$ open -h UIView.h -s iPhoneOS10.2
$ open -a BBEdit -h NSTask

If the search term is ambiguous open will list all the options.