Home Life Legos Tech Mac Graveyard
magnusviri

2008-01-01

OS X Terminal

Some time between 2003 and 2008 I wrote this. It's a through discussion of the Terminal and UNIX commands.


Before the GUI, there was the terminal

This page assumes you don't have much knowledge of the Terminal but it jumps right in with in-depth information, like why you might want to use sudo rm -- -r / instead of just sudo rm -r /, or what happens when you create a file in the Finder with / in the name, what {,} does in the shell, the difference between ditto and cp -R, why vsdbutil -d is evil, etc. So I hope it offers something to the newbie and an experienced user.


What and why

"Command line", "command line interface", "terminal", and "shell" are words that describe a text user interface. You interface with the computer by typing text messages on a keyboard. The computer displays text messages back at you.

Why would you want to learn how to use the terminal?

For all of these reasons, I'm going to discuss Terminal from the standpoint that we are going to replace the Finder. It can be used as a references, but each section builds on what comes before. While most of this applies to many Unix systems this page is Mac OS X centric. Some commands are only available on Mac OS X.


Where is Terminal?

You can get a Terminal many ways. Probably the most obvious is to open /Applications/Utilities/Terminal.app. If you have X11 installed, open the X11 app and it will open an xterm, which behaves a bit differently than Terminal.app. You can also download several 3rd party terminal applications like iTerm.

Or you can go hardcore and type ">console" at the login prompt instead of your normal username and password (you might have to click the "Other" button if you are using a list of users at loginwindow). You wont be able to launch GUI applications.

Although you probably rarely want to do this (unless you are a system administrator), you can boot to single user mode by holding command-s right after the startup chime. Once the pinwheel appears, it is too late and it has gone to multi user. You will need to restart and try again. If it keeps not working, try holding down option at startup. Does it ask for a password? If it does, you will need to turn Open Firmware password off (Google is your friend).

I'm going to assume you are using Terminal.app.


New window

Finder can have many windows open at once, Terminal can too. Open a new Terminal shell with command-n. This is equivelent to the new Finder window command (and is the same keyboard short cut even).


Find out where you are

Just like a Finder window always is located in some folder, the Terminal is always at some folder as well. When opening a new Terminal window, it will most likely be located in your home directory (aka $HOME, or ~/).

If you are unsure where you are, you can type pwd. This is equivalent to command clicking a Finder window titlebar, only the pwd version can be copied.

Example:

  [Computer:/Library] james% pwd
  /Library

Notice that my prompt actually tells me where I am.

  [Computer:*/Library*]

It will not always tell you the full path though, so pwd is still needed.


Look at a file

To print out an entire file:

  cat <filename>

To view a file page by page:

  less <filename>

or

  more <filename>

("less is more")


Create an empty file

To create an empty file:

  touch <newfilename>

Finder can't create files.

The file system keeps track of when files were last changed. If you use touch on a file that already exists, the file contents will not be changed, but the modification date will change the current time (thus, it was "touched" but not changed).


Erase the contents of a file (piping)

To erase (or create a new file).

  echo -n > <filename>

To erase a file and put some text in it.

  echo "Your text here" > <filename>

To append a line to the end of a file:

  echo "Your text here" >> <filename>

Notice the difference is two >>. Be careful with this because the last thing you want to do is accidentally erase a file by only putting one > (I've done it).


Arguments/parameters

Commands take options and other arguments and parameters. For example, touch takes the name of the file to create. The name of the file is an argument or parameter.

Unix commands can be given options telling it to do something different. Those are also arguments and parameters. For example, touch -c <filename> will do something different than the default behavior. When a parameter is an option, like -c, it is also called a flag or a switch.

Sometimes multiple flags can be combined (in any order usually), like this:

  pwd -PL

Or they can be specified individually:

  pwd -P -L

Some options use two dashes --option-name. Two dashes alone -- (no letters after it) indicates there are no more flags. This is so that you can to do something to a file that begins with -. For example:

  [Computer:~/] james% touch -c
  usage: touch [-acfm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...

Oops, it didn't work but instead told me how I'm suppose to use the command. However, this works:

  touch -- -c

This creates a file named -c.


Looking at the contents of a folder

Naturally the Finder always shows files and folder and gives you information about them and even allows you to "Get Info" so you can see more details and make some changes. You can get the same lists and, in fact, more information than the Finder shows. Use ls (list) for the basic list. Here are some of the different options:

Shows a quick directory list:

  ls

Show a quick list, only one item per line:

  ls -1

Show detailed list:

  ls -l

Show all files including hidden ones:

  ls -Al

Sort the listing by size (largest first):

  ls -S

Sort by time, reverse order (newest last):

  ls -tr

Look at the contents all of the sub-directories (recursive):

  ls -R

Show the locked state of a file (an extra column is shown):

  ls -ol

Show the contents of :

  ls -l <dirname>

Show but not the contents:

  ls -dl <dirname>

How do you read the output of ls -l?

  drwx------   37 james  james    1258 Oct  7 23:01 Desktop
  -rw-r--r--    1 root   james   17920 Oct  1 09:55 Desktop DB
  -rw-r--r--    1 root   james  113090 Oct  1 21:28 Desktop DF
  drwx------    9 james  james     306 Jul 15 22:05 Documents
  drwx------   53 james  james    1802 Jun 21 17:38 Library
  drwx------   50 james  james    1700 Sep 30 12:44 Movies
  drwx------   20 james  james     680 Jul  9 20:22 Music
  drwx------   25 james  james     850 Sep 10 22:52 Pictures
  drwxr-xr-x    6 james  james     204 Oct  8 15:49 Sites

The first character is the type of file/folder. - d Directory. - l Symbolic link. - - Regular file.

The next nine characters rwx------ are the permissions.

The first 3 characters are the "owner" or "user" permissions, the next 3 are the group permissions, the last 3 are permissions for anyone who has access to the computer if it has multiple accounts ("other" or "world").

See chmod below for more info on permissions.

The next number tells you how many files are in a folder (including . and .. or any other hidden files) or the number of hard links a file has (don't worry about what that is, by default it will almost always be one).

Next is the user that owns the file. Then you get the group of the file. Then the filesize in bytes. Then you get the last modification date and time. Then you finally get the filename. If the file is a symbolic link (similar to a Finder alias), there will be an arrow showing what file the link points to (try running ls -l / to see some links).


More options available

To find out what a flag does, or to find out what other flags there are, use man <commandname>. For example:

  man ls

For more information on man, see the next tutorial on finding out everything about commands.


Folder separator character

The forward slash "/" is the folder separator character. No file or folder name can have this character in it. If you create a filename with this character in the Finder, it will let you. But if you look at it from the command line the "/" will be converted to ":".

Example:

  drwxr-xr-x    2 james  james     68 Oct  7 00:02 adsf:adsf

(In the Finder it appears as "asdf/asdf")

Note, in the Finder, ":" is the folder separation character. You can't create a file with that character in the Finder. But you can in Terminal. If you do, the Finder will display the ":" as a "/". Ok.


/ = root of boot disk

So, look at the root of the hard disk:

  [Computer:~] james% ls /
  Applications
  Desktop DB
  Desktop DF
  Developer
  Icon?
  Library
  Network
  System
  TheVolumeSettingsFolder
  Users
  Volumes
  automount
  bin
  cores
  dev
  etc
  mach
  mach.sym
  mach_kernel
  private
  sbin
  tmp
  usr
  var

Surprised? Where did all those things come from? This is what is at the root of your hard disk! The Finder only shows these though:

Applications, Developer, Library, System, Users

That is because it is protecting you from all of that Unix stuff and ls doesn't.


Hidden files

If a file begins with a period "." (like ".DS_Store") it is invisible in the Finder and with ls. ls -A (or ls -a) is how you view all of the invisible stuff. The Finder will never show you invisible items. Also, the root directory, "/", contains hidden files that are not visible by the Finder but don't begin with ".". The Finder looks at the file "/.hidden" (if it exists) and hides everything listed in the file. Finder also has meta file information that can hide a file that doesn't begin with a dot. You can view this meta information with something like GetFileInfo.

  [Computer:/Library] james% /Developer/Tools/GetFileInfo /private
  directory: "/private"
  attributes: aVbstclinmedz
  created: 04/17/2006 23:27:37
  modified: 10/02/2006 20:59:54

Notice the V in the attributes line. Capital means it is invisible. (Type "man GetFileInfo" for more information-you must have the developer tools installed.)

To make a file invisible the Unix way just use a period as the first character in the name. Note, the Finder will not let you do this.

To make a file or folder invisible to the Finder, use SetFile.

  [Computer:~] james% /Developer/Tools/GetFileInfo /Users/james/Desktop
  directory: "/Users/james/Desktop"
  attributes: avbstclinmedz
  created: 09/13/2006 09:59:23
  modified: 10/07/2006 14:57:12
  [Computer:~] james% /Developer/Tools/SetFile -a V /Users/james/Desktop
  [Computer:~] james% /Developer/Tools/GetFileInfo /Users/james/Desktop
  directory: "/Users/james/Desktop"
  attributes: aVbstclinmedz
  created: 09/13/2006 09:59:23
  modified: 10/07/2006 14:57:12

To make it visible, use a lowercase "v".

  [Computer:~] james% /Developer/Tools/SetFile -a v /Users/james/Desktop


Absolute paths

An absolute path BEGINS with "/". It specifies the location of a file or folder starting with the root of the hard disk and descending folders. So /Users/mac starts at the root of the hard disk and descends into the "Users" folder and then the "mac" folder.

You don't need to be IN a folder to look at it or do anything in it. This makes Terminal more powerful than Finder, because in the Finder you always have to be in a folder to do anything in it.

For example, my prompt shows I'm in the /Library folder, and I can view other folders without moving to them:

  [Computer:/Library] james% ls /Users
  Shared  james   matt

  [Computer:/Library] james% ls -1 /Applications
  Address Book.app
  AppleScript
  Automator.app
  ...
  iPhoto.app
  iSync.app
  iTunes.app
  iWeb.app
  iWork '06


/Volumes vs /

Why are servers and extra hard disks and media located in /Volumes but the root of the hard disk is at /? Does this mean they are copied to the boot hard disk? Not at all. A Unix file system is actually not an exact representation of the boot hard disk, like you would think. Rather, it is a "file system", meaning all files (and some things that AREN'T files) are accessible from the file system.

To do that, the OS has to "put" everything somewhere. Long ago it was decided that the boot hard disk was the root of the file system, or "/". All other hard disks have to be "mounted" somewhere in that file system (see below for instructions how to do that). Apple decided to mount them in /Volumes. Other Unixes put them in different places and is configurable.

This isn't the only goofy Unixy thing you will encounter either. The contents of the /dev directory are not files or folders either, but I'll just say they are "special" things that need to be accessible in the "file system". You don't want to try to edit them with a text editor.


Moving around

Just like in the Finder where you can move around the folder hierarchy, you can do the same in Terminal using the cd (change directory) command. Note, in Finder you have folders. In Unix you have directories. They are the same thing. However, you earn geek points by using the word directory (it has more syllables).

Go to (valid paths will be discussed below):

  cd <path>

Go to your home folder:

  cd

Go to the parent directory (in Finder do this by pressing command-up arrow):

  cd ..

Toggle to the last location (in Finder click the back and forward button at the top of the window):

  cd -

You can also use pushd and popd if you plan on toggling around a lot. It was fun when I first used it, but then I realized trying to figure out where I would go the next time I typed popd took longer than just typing cd <path>.

  [Computer:~] james% pushd /
  / ~
  [Computer:/] james% pushd Library/
  /Library / ~
  [Computer:/Library] james% pushd /Applications/
  /Applications /Library / ~
  [Computer:/Applications] james% popd
  /Library / ~
  [Computer:/Library] james% popd
  / ~
  [Computer:/] james% popd
  ~
  [Computer:~] james%

You don't have to cd to a folder every time you want to look at it. When I watch new people, they cd to a folder then immediately ls, then cd to another folder inside it, then ls again, the cd again, etc. If you know the name of the path, just type the full path (or use tab completion-see below). If you don't know exact paths, and if the folders aren't full of tons of files, you can ls -R to see everything inside of where you are going. Then you can write one cd.

Another little Mac OS X trick that I use often is to type open . (see below for more info). This will open the current directory in the Finder. Then I switch to the Finder column view navigation (command-3 to get that view). Then I use the arrow keys to find what I want. I grab the mouse and drag the location of that window back into the terminal. Click in the Terminal to bring it forwards. Then I press ctrl-a (see below again) to go to the beginning of the line, then I type cd. Kinda tricky, but with practice, it is very quick (much quicker than cd ... ; ls ; cd ... ; ls ; cd ...).


Multiple arguments

Some commands, like cd, only takes one argument. However, others, like ls, can take as many as you want. For example:

  [Computer:/Library] james% ls -1 /Users/ /Applications/
  /Applications/:
  Address Book.app
  AppleScript
  Automator.app
  ...
  iPhoto.app
  iSync.app
  iTunes.app
  iWeb.app
  iWork '06

  /Users/:
  Shared
  james
  matt

Most commands will take many arguments and do the action on all of them.


Relative paths

A relative path can begin with either a name of a file or folder like "Library", "./", "../", or "~/".

Examples:

The folder named "Library" in the current working directory:

  Library

The current directory:

  .

or

  ./

Parent directory:

  ..

or

  ../

Your home folder:

  ~/

More examples of valid (but maybe a bit odd) relative paths (the first cd . goes to the current directory, basically it doesn't move):

  [Computer:~] james% cd .
  [Computer:~] james% cd ..
  [Computer:/Users] james% cd ../Library/
  [Computer:/Library] james% cd ../
  [Computer:/] james% cd Library/
  [Computer:/Library] james% cd ../Library/
  [Computer:/Library] james% cd ../Library/./.././Library/
  [Computer:/Library] james% cd ~
  [Computer:~] james% pwd
  /Users/james

Kinda odd that I'm going in and out of the Library folder, but it is all ok and works.


Tab completion

This needs to be set up

That is a cool shell feature. The way it works is that you type part of a path and then you type the TAB key and the shell tries to figure out what file or folder you want and it fills it in if it can figure it out. For example, if you type "~/M" and press TAB, you have two options "~/Movies" or "~/Music". Because there are 2 options, the shell doesn't know what you want, and it wont complete it but will instead beep at you. Some shells will actually print out the options:

  [Computer:~] mac% cd ~/M
  Movies/ Music/
  [Computer:~] mac% cd ~/M

To give the shell a better chance of figuring out what I really want, I type one more character, the "o". Then I hit TAB and the shell automatically fills in the rest:

  [Computer:~] mac% cd ~/Movies/

Sometimes, if tab completion doesn't work, it means you have a misspelling in your path.

Tab completion works with commands too:

  system_<tab>

becomes

  system_profiler


Spaces in paths (kinda like "Pigs in Space", ok, maybe not)

Old Unix didn't use spaces in paths because spaces means "something new" on the command line. So "/Library/Application Support" is 2 things in Unix: "/Library/Application" and "Support".

Mac OS 9 and below used spaces as if they were saying to all the Unix and DOS people: "Hahaha! We got em and you don't!". But now we have OS X, and now we are cursed with all those old legacy spaces.

Sp to use spaces on the command line, you have two options. You either put quotes around the path or you "escape" the spaces by placing a backslash in front of it, like this: " ". Ok, this really shows that Unix did allow spaces, it just wasn't the norm. These options are the same:

  cd "/Library/Application Support"
  cd /Library/Application\ Support

Tab completion will really help out here. It will automatically escape your spaces. And if it doesn't tab complete, there is a good chance you typed in the path wrong anyway.


Case insensitive but case preserving

This is a attribute of the HFS+ file system, which is like rude jelly.

Technically, in HFS+, /etc is the same as /ETC. So both of these will work:

  ls /etc/
  ls /ETC/

However, tab competition is case sensitive, so if get the case wrong, it wont complete for you.

Does nothing (but beep):

  ls /E<tab>

Works:

  ls /e<tab>


{,} Wildcard

Wildcards are like jokers, they represent things that they aren't. The {,} wildcard allows you to select multiple options, each separated by a comma. In this example, the ls command lists both Movies and Music:

  [Computer:~] james% ls -dl ~/M{ovies,usic}
  drwx------   50 james  james  1700 Sep 30 12:44 /Users/james/Movies
  drwx------   20 james  james   680 Jul  9 20:22 /Users/james/Music

In this example, the ls command lists /mach, /mach.sym, and /machkernel. Each is separated by a command, first "" (blank), then ".sym", then "kernel":

  [Computer:~] james% ls -l /mach{,.sym,_kernel}
  lrwxr-xr-x   1 root  admin  9 Oct  2 20:59 /mach -> /mach.sym
  -r--r--r--   1 root  admin   615480 Oct  2 20:59 /mach.sym
  -rw-r--r--   1 root  wheel  8545336 Sep 27 02:13 /mach_kernel

See below for more examples.


* Wildcard

The wildcard I use most is *.

Everything:

  *

Everything in /:

  /*

Everything in / that begins with "a" (including "a"):

  /a*

Everything in / that ends with "a" (including "a"):

  /*a

Everything in / that begins and ends with "a" (including "aa"):

  /a*a

Everything in / that has an "a" in it (including "a"):

  /*a*

The only exception to "everything" is dot files. To select files that begin with ".", you have to specify the dot:

Everything hidden in /:

  /.*

Everything hidden in / that begins with "a" (including "a"):

  /.a*

Everything hidden in / that ends with "a" (including "a"):

  /.*a

Everything hidden in / that begins and ends with "a" (including "aa"):

  /.a*a

Everything hidden in / that has an "a" in it (including "a"):

  /.*a*

If you want to select both at the same time, use {,} with *:

  ls -l {,.}*

A more realistic example:

  ls thumb*.jpg

You can also use it in paths. For example:

  [Computer:~/] james% ls -l /Library/*/Apple
  total 0
  drwxrwxr-x    3 root  admin  102 Jan 13  2006 Automator
  drwxrwxr-x    7 root  admin  238 Jan 14  2006 Chinese Input Method Plugin Samples
  drwxrwxr-x   13 root  admin  442 Aug  1 11:58 Developer Tools
  drwxrwxr-x    3 root  admin  102 Jan 14  2006 Grapher
  drwxrwxr-x    5 root  admin  170 Oct  4 16:55 Remote Desktop
  drwxrwxr-x    4 root  admin  136 Aug  1 18:02 System Image Utility
  drwxrwxr-x   11 root  admin  374 Aug 17 21:08 iChat Icons

What folder am I looking at though? I don't know. Here is how I find out:

  [Computer:~/] james% ls -ld /Library/*/Apple
  drwxrwxr-x   9 root  admin  306 Oct  4 16:55 /Library/Application Support/Apple

What if there were multiple folders found.

  [Computer:~/] james% mkdir /Library/Preferences/Apple
  [Computer:~/] james% touch /Library/Preferences/Apple/hahaha
  [Computer:~/] james% ls -l /Library/*/Apple
  /Library/Application Support/Apple:
  total 0
  drwxrwxr-x    3 root  admin  102 Jan 13  2006 Automator
  drwxrwxr-x    7 root  admin  238 Jan 14  2006 Chinese Input Method Plugin Samples
  drwxrwxr-x   13 root  admin  442 Aug  1 11:58 Developer Tools
  drwxrwxr-x    3 root  admin  102 Jan 14  2006 Grapher
  drwxrwxr-x    5 root  admin  170 Oct  4 16:55 Remote Desktop
  drwxrwxr-x    4 root  admin  136 Aug  1 18:02 System Image Utility
  drwxrwxr-x   11 root  admin  374 Aug 17 21:08 iChat Icons

  /Library/Preferences/Apple:
  total 0
  -rw-r--r--   1 james  admin  0 Oct  7 00:36 hahaha

Well, it tells me what the folders are. The above command is the same as just giving ls 2 paths (really, that is exactly what the shell does, it gives the command the 2 paths, the command doesn't ever see the wildcard):

  ls "/Library/Application Support/Apple" "/Library/Preferences/Apple"

What if I try to cd to that?

  [Computer:~/] james% cd /Library/*/Apple
  tcsh: /Library/*/Apple: Ambiguous.

Oops, it wont let me. This is because cd only takes on argument.

Tab completion doesn't work with wildcards.


Root

Do something as root (sudo will ask for password)

  sudo ...

Become root user

  sudo -s

sudo means super user do. Don't mix this up with su, which means substitute user.

If you have set a root password (by default Mac OS X does not have one set), then you can also do this:

  su root

When you are root, you can freely su to other user accounts.

To leave root, type exit.


Moving files/folders

Move a file:

  mv <filename> <newlocation>

<newlocation> must be a folder that exists already, mv will not create folders for you.

Move several files:

  mv <filename1> <filename2> <newlocation>


Renaming files/folder

The rename command is exactly the same as the move command. The idea is that you are "moving it from one name to another".

  mv <oldfilename> <newfilename>

Or using the {,} wildcard:

  mv {<oldfilename>,<newfilename>}

Example:

  mv oldname newname

The above command is exactly the same as the following (only less typing, and geekier):

  mv {old,new}name

Example renaming:

  [Computer:~] james% mkdir oldname
  [Computer:~] james% ls -dl *name
  drwxr-xr-x   2 james  james  68 Oct 10 18:06 oldname

  [Computer:~] james% mv oldname newname
  [Computer:~] james% ls -ld *name
  drwxr-xr-x   2 james  james  68 Oct 10 18:06 newname

  [Computer:~] james% mv {new,newer}name
  [Computer:~] james% ls -ld *name
  drwxr-xr-x   2 james  james  68 Oct 10 18:06 newername

You can rename and move at the same time:

  [Computer:~] james% touch blah
  [Computer:~] james% mv blah newername/bla
  [Computer:~] james% ls -l *name
  -rw-r--r--   1 james  james   0 Oct 10 18:06 bla


Making directories

Create a new directory:

  mkdir <dirname>

Create a bunch of new directories inside of each other

  mkdir -p <dirname/dirname/dirname>

Create a bunch of new directories

  mkdir -p <dirname1> <dirname2> <dirname3>


Deleting

WARNING, THESE CAN NOT BE UNDONE (unlike move to Trash)!

Delete a file:

  rm <filename>

Delete a directory (if empty):

  rmdir <dirname>

Delete a directory (doesn't matter if it is empty or not-DANGEROUS, BE CAREFUL!!!!!!!!!!!!!):

  rm -r <dirname>

Delete a bunch of files:

  rm <file1> <file2> <file2> ...

Erase your home folder (not wise):

  rm -r ~/

Erase your boot disk (I can admit to doing this once on a test box on purpose just to see what would happen, it was fun!):

  sudo rm -r /

Secure delete:

  srm <file>

Delete a file named "-r":

  rm -- -r

How to cause rm -r to fail:

  [Computer:~/asdf] james% touch -- -r
  [Computer:~/asdf] james% rm -r *
  usage: rm [-f | -i] [-dPRrvW] file ...
  unlink file
  [Computer:~/asdf] james% ls -l
  -rw-r--r--    1 james  james 0 Oct 10 22:40 -r

Oops, that can cause a lot of havoc to scripts. How to safely delete files:

  [Computer:~/balladsf] james% rm -r -- *

I suppose I should update all of my scripts that only use rm -r <path>.


Open stuff

Open a file:

  open <filename>

Open a file with TextEdit:

  open -e <filename>

Open a file with default text editor (determined by LaunchServices-the "Open With" setting in Get Info):

  open -t <filename>

Open a file with a particular application:

  open -a <path to app> <filename>

Open a directory (in Finder):

  open <directory>

Launch an application:

  /path/to/application.app/Contents/MacOS/appname

Example

  /Applications/Safari.app/Contents/MacOS/Safari

If you run an app using the above technique, it is no different than running any other command, such as rm. The difference is that rm runs and quits very quickly (well, sudo rm -r / will probably take awhile). If you want to launch the app and keep using the terminal, then just "detach" it by putting a space and ampersand at the end, like this:

  /Applications/Safari.app/Contents/MacOS/Safari &

Any app can print to the terminal, and that is exactly what command line apps do. GUI apps can do that too, but most do not. So if you use the ampersand, and text suddenly appears in your terminal, it could be from the command you detached. The text does nothing, so you can ignore it if you want (and if you can-you may need to clean up your display).


Copy

Copy a file

  cp filename new_location/

Duplicate a file/directory:

  ditto <copy contents of dir> <to dir>
  cp -R <copy dir> <to dir>

ditto and cp behave differently if the destination folder exists! ditto is a Mac OS X utility.

Create a duplicate of the Desktop folder and name the new folder Desktop2 (Desktop2 does not exist):

  ditto ~/Desktop ~/Desktop2

Same as the ditto command except a different name (Desktop3 does not exist)

  cp -R ~/Desktop ~/Desktop3

Desktop2 exists, copy the contents of Movies into Desktop2:

  ditto ~/Movies ~/Desktop2

Desktop3 exists, make a new folder named Movies in Desktop3:

  cp -R ~/Movies ~/Desktop3

Notice how cp and ditto behave very different if the destination folder exists! cp will create a new folder in the destination. ditto will copy the contents of the source folder to the destination folder! Very different.


File owner or group

Every file and folder is "owned" by a user account. Use chown and chgrp to change who owns what. You can do this in the Finder with the "Get Info" dialog.

Change the owner:

  sudo chown <username> <path>

Change the owner of everything in a folder:

  sudo chown -R <username> <path>

Multiple owners can modify a file if they are in the same group as the file. That is what group is used for.

Change the group:

  sudo chgrp <groupname> <path>

Change the group of everything in a folder:

  sudo chgrp -R <groupname> <path>


Change permissions

Each file has a set of permissions associated with it. The permissions is called the "mode". This was talked about already in the ls section. However, there is more to know!

You do some of this in Finder in the "Get Info" dialog. The part you can't do in the Finder is change the x setting. Changing the x setting can bust stuff if you don't know what you are doing.

Change the mode:

  chmod <mode> <path>

Change the mode of everything in a folder:

  chmod -R <mode> <path>

Modes can be specified two ways: what you want done, or a number that represents all of the permissions.

The "what you want done" method is easy. You specify the "user" (aka owner), the group, or "other" (aka world) and then the operation (- or +) and the permission you want changed (r, w, or x). So if I wanted to give other write permission to /etc/crontab (which is a very bad idea), I use this command:

  sudo chmod o+w /etc/crontab

If I wanted to remove world write permissions for every user to every file (another bad idea), I use the command:

  sudo chmod ugo-w /

A number permission is fairly easy to read and you only use a few combinations anyway.

  r means read access (value of 4)
  w means write access (value of 2)
  x means execute if file, search if folder (value of 1)

Add up the values to get a number (octal) representation of the permissions:

  rwx = 7
  rw- = 6
  r-x = 5
  r-- = 4
  -wx = 3
  -w- = 2
  --x = 1
  --- = 0

You will usually only use 7, 6, 5, and 4.

Then because there are 3 categories of users, there are 3 of these numbers in a row:

  rwxrwxrwx = 777
  rwxr-xr-x = 755
  rw-r--r-- = 644

Folders and executable binaries or scripts should be set to this permission:

  775 (rwxrwxr-x)

or

  755 (rwxr-xr-x)

It is common for files (that aren't scripts or executables) to be set to this permission:

  664 (rw-rw-r--)

or

  644 (rw-r--r--)

Usually 7 and 5 go together (rwx and r-x) and 6 and 4 go together (rw- and r-). The only times I've seen 766, 744, 655, or other combinations like that is when someone didn't know what they were doing (733 on a folder is actually a drop box, so old modes aren't always wrong, just rare).

There is also more mode information like SUID bit, sticky bit, etc.,. Just ignore them because you shouldn't mess with them if you don't already know what they are. That extra mode information is at the beginning of the number, making it a 4 digit number. Usually that number is 0 (meaning no extra info). So you might see 0755 and 0644 for example.

If you see 0777 (rwxrwxrwx) that is a good sign somebody didn't know what they are doing. It means that anyone who has an account on your computer can make changes to that file or folder. In most cases, this doesn't matter because Mac OS X is sold to consumers who only give accounts to their family. However, in a multiuser environment like a school lab or a server, 0777 is not a good idea. If you look at /private/tmp, you will see this permission: drwxrwxrwt (1777). Notice the "t"? This is a safe way of doing 777. Yes you can chmod +t <file>.


File locking

In the Finder, you can select "Get Info" on any file or folder and there is a checkbox to "Lock" it. This makes it so you can't delete or change the file. You can do this in the Terminal too.

Lock a file/folder:

  chflags uchg <path>

Lock a directory and everything in it:

  chflags -R uchg <path>

Unlock a file:

  chflags nouchg <path>

Unlock a directory and everything in it:

  chflags -R nouchg <path>


Recursive

Command line tools do things to files and directories. Several of them will do things to everything in a directory (recursion). Getting a command to behave recursively depends on the command.

Some tools use -r, some -R. There is kinda a pattern. Most tools use lower case ("-r"). The tools (that I know of) that use upper case are ls, cp, and the ch* commands (chmod, chown, chgrp, and chflags). ls -r just reverses the sort order, so if you use it, no harm will be done (you just don't get what you wanted). chmod -r will remove read permissions (oops). The other ch* commands don't respond to -r. Again, the other tools use lower case ("-r").

If in doubt, try man <toolname> and look at the available options.

When using recursion with the * wildcard, there is a slight difference.

  chmod -R someuser ~/

The above is different from:

  chmod -R someuser ~/*

The first specifies the ~/ folder and all of its contents. The second specifies all of the contents of ~/, but not the folder ~/.


Make symbolic link

This is similar to a Finder alias, but they are NOT the same.

  ln -s <realfile> <linkfile>

When you ls a symbolic link, it looks like this:

  lrwxr-xr-x    1 root   admin 11 Apr 18 07:29 tmp -> private/tmp

Notice the first character is "l". Also notice the arrow pointing right. When creating a symbolic link, just remember that the arrow points the opposite direction: realfile <- linkfile (ls is linkfile -> realfile, see how it is different?)

If you want to create a real Finder alias from the command line, use OSXUtils.


Find a file

  find <path> <criteria> <commands>

Examples:

  [Computer:/etc] james% sudo find /private/etc -name "host*" -print
  /private/etc/hostconfig
  /private/etc/hosts
  /private/etc/hosts.equiv
  /private/etc/hosts.lpd

  [Computer:~] james% find ~/.Trash -name ".DS_Store" -print -delete
  /Users/james/.Trash/.DS_Store
  /Users/james/.Trash/build/.DS_Store
  /Users/james/.Trash/build/Release/.DS_Store
  /Users/james/.Trash/build 23-02-55/Release/.DS_Store
  /Users/james/.Trash/untitled folder/.DS_Store

  [Computer:~] james% find ~/.Trash -name ".DS_Store" -print -delete
  [Computer:~] james%

In the last command there were no files left to delete!

Finding files vs directories and executing a command on the found set:

  find /var/radmind/transcript -type d -exec /bin/chmod 770 '{}' ;
  find /var/radmind/transcript -type f -exec /bin/chmod 660 '{}' ;

find can find files using many qualifiers, like permissions, size, path, modification date, older, newer, etc. man find to see the list.

You can also use Spotlight from the command line!

  mdfind <text to find>

Example:

  [Computer:~] james% mdfind modo
  /Library/Application Support/Luxology/Documentation/help/pages/Stretch.html
  /Library/Application Support/Luxology/Documentation/help/pages/Subdivide.html
  /Library/Application Support/Luxology/Documentation/help/pages/Subdivision_Level.html
  ...
  /Users/james/Library/Mail/IMAP-james@-----.com/mail/sent-mail.imapmbox/Messages/347181.emlx
  /Users/james/Library/Mail/IMAP-james@-----.com/mail/sent-mail.imapmbox/Messages/347819.emlx
  /Users/james/Library/Mail/IMAP-james@-----.edu/Sent Messages.imapmbox/Messages/348825.emlx
  /Users/james/WherezModo/source/Controller.h
  /Users/james/WherezModo/source/Controller.m
  /Users/james/Desktop/unix/Tutorial

Here is another interesting example searching for world writable locations (output seriously shortened because it was so long.):

  [Computer:~] james% /usr/bin/find / ! -type l -perm -2
  /.Trashes
  /Applications/Adobe/Adobe Version Cue CS2/config/StartupOptions.xml
  /Applications/DivX Converter/DivX Converter.app
  /dev/tty
  /Library/Application Support/Adobe/Adobe Registration Database
  /Library/Audio/Apple Loops Index
  /Library/Caches
  /Library/Internet Plug-Ins/DRM Plugin.bundle
  /Library/Preferences/Adobe Systems
  /Library/Printers/EPSON/CIOSupport/Preferences
  /Library/Printers/Lexmark/Preferences
  /Library/ScriptingAdditions
  /Library/ScriptingAdditions/Adobe Unit Types
  /private/etc/opt/cisco-vpnclient
  /private/tmp
  /private/tmp/mysql.sock
  /private/var/run/asl_input
  /private/var/spool/samba
  /private/var/tmp
  /private/var/tmp/mds
  /Users/Shared
  /Users/Shared/GarageBand Demo Songs
  /Volumes

And this command turned up some interesting finds (output pruned again):

  [Computer:/System/Library] james% find . -type f -perm -1 -print
  ./Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister
  ./CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/MacOS/LockScreen
  ./CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart
  ./CoreServices/Software Update.app/Contents/Resources/SoftwareUpdateCheck

I tested a few and a few of the commands actually did things:

  [Computer:/] james% "/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdateCheck"
  2006-10-12 23:04:23.754 SoftwareUpdateCheck[11716] Checking for updates

(The software update window appeared and showed my all of my out of date Apple software!)

I cheated, I already knew about this one:

  [Computer:] root# /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -help

  kickstart -- Quickly uninstall, install, activate, configure, and/or restart
  components of Apple Remote Desktop without a reboot.

  kickstart -uninstall -files -settings -prefs

  -install -package <path>
  ...

I knew about this one too:

  [Computer:/] james% /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister
  lsregister: [OPTIONS] [-domain { system | local | user | network }]... [path]...
  Search the paths for application bundles and add each found item to the Launch
  Services database.  For domain specifications, ask CF for the list of application
  locations in the given domain(s).

  -kill     Reset the global Launch Services database before doing anything else
  -lint     Print information about plist errors while registering bundles
  -convert  Register apps found in older LS database files
  -lazy n   Sleep for n seconds before registering apps if the local cache
  is aleady populated.
  -r  Recursively register directory contents, do not recurse into
  packages or invisible directories.
  -R  Recursively register directory contents, including the contents
  of packages and invisible directories.
  -f  force-update registration info even if mod date is unchanged
  -v  Display progress information.
  -dump     Display full database contents after registration.
  -h  Display this help.


Clear the screen

There is the command:

  clear

And the keyboard shortcut:

  ctrl-l

And in Terminal.app you can use command-K to clear the screen and the "scrollback". This is sometimes helpful if you need to run a command that prints a lot of text, because you can press home key to see the top of the output and page down to read it. This is much easier than trying to scroll up and see where you started the command with a full scrollback. You can set the scrollback length to unlimited in the Terminal prefs if you have really long winded commands.


History

The shell keeps track of what commands you type. This is a feature of the shell, not the application. This will print out a list of all remembered commands:

  history

You've also got keyboard shortcuts:

  up arrow - history up one word
  down arrow - history down one word

When using the up arrow, you can use the ctrl-a key to jump to the beginning of the line and then esc-f to go forward each word until you get where you want to change it (I hate watching people hold down left arrow to the start of the line).

And there is history search:

  <typeletters> esc-p - history search backwards (does not work on BASH)
  <typeletters> esc-n - history search forwards (does not work on BASH)

History search is similar to tab completion, you can start typing a command, but instead of hitting TAB to complete the command, use esc-p and it will complete it with whatever it finds in the history that matches the first part you've typed already. Keep pressing esc-p or esc-n to move up or down through the history.

The history is saved to a file, either ~/.bashhistory or ~/.tcshhistory. This is a VERY GOOD REASON to NEVER TYPE A PASSWORD. If you do, you want to close all your terminal windows and then erase the history then open a new terminal window, then close the old one, then close the new one. Only after doing that complicated routine is the password really gone. Really, just don't type your password in a command.

This is why you need to do all of that to clear the history (and why the history isn't 100% accurate).

When a new terminal window is opened, it reads the history file and then every command you type after that is remembered by that window (but not written to the history file). When you close the window, the remembered history is written to the history file, including the old history it loaded when you opened the window.

If you open 2 Terminal windows at the same time, each will read the history file. If you type "bad things" in window A and close it, it will save "bad things" to the history file. If you then type "good things" in window B, then close it, it will OVERWRITE the history window A just saved with window B's history, thus erasing "bad things".

It is kinda complex. Just don't expect the history to remember every command you type. If you catch the hang of it, you can at least be aware of when you accidentally blow away the history (which I've done in agony). Of course you can avoid doing that by just keeping one Terminal window open, but I can't stand that and typically have a minimum of 3 open on my work computer, and sometimes as many as 10.


Bindkey

Typing in the terminal can be a slow process, especially if your key repeat rate is slow (mine is all the way up-yours should be too). Luckily, there are some short cuts that let you move around quickly.

First of all, go to "Terminal" menu, "Window Settings", "Emulation" pop-up, and check "Option click to position cursor". Now you can option click and the cursor will try to move to that location. Terminal.app does this by using the keyboard arrow keys to move the cursor around (or something). What that means is that if it hits a tab, or some other strange character, the cursor will not actually move to the location you clicked, but it will be off. So you may have to option click a few times to get it to the right spot. It is better than just the arrow keys so be happy.

Moving around:

  ctrl-a - move to beginning of line
  ctrl-e - move to end of line
  esc-b - skip backwards a ward at a time
  esc-f - skip forwards a word at a time

Cut and paste:

  ctrl-d - deletes the charcter **after** the cursor (opposite of the delete key)
  esc-backspace - delete letters before the cursor to the next space (word delete)
  esc-d - delete letters after the cursor to the next space (word delete)
  ctrl-u - delete whole line
  ctrl-k - delete from cursor to end of line)
  ctrl-w - delete from cursor to begin of line)
  ctrl-y - yank, puts back what ctrl-u/k/w removed (acts like paste)
  esc-y - cycle through yank
  esc-d

Very important:

  ctrl-c - cancel

End of Line:

  ctrl-d - when in input mode (haven't mentioned that yet, but `grep a` is an easy way to get there), exit with cntl-d

Change case:

  esc-u - make word uppercase
  esc-l - make word lowercase

To see all options, type bindkey if you are using TCSH or bind -p if you are using BASH.


Open a CD tray

  drutil eject

Use scripts that synchronize their times with their IP and send the command via Apple Remote Desktop that open and close the trays of rows of computers in front of large crowds of Apple system admins in order to pretend that you are Steve Hayman.


Mount a disk image file (dmg)

  hdiutil mount <file.dmg>

  [Computer:~/Desktop] james% hdiutil mount *dmg
  Checksumming Single Volume (Apple_HFS : 0)...
  .................................................................................
  Single Volume (Apple_HFS : 0): verified   CRC32 $A1000E0F
  verified   CRC32 $DAAF9163
  /dev/disk2    /Volumes/SomeDMG

Unmounting:

  [Computer:~/Desktop] james% hdiutil unmount /Volumes/SomeDMG
  "disk2" unmounted successfully.


Ignoring permissions of a mounted volume

Apple allows users to bypass Unix permissions on mounted volumes. To check to see if permissions are ignored on a volume:

  vsdbutil -c /Volumes/Diskname

To ignore permissions on the volume:

  vsdbutil -d /Volumes/Diskname

To use Unix permissions on a volume:

  vsdbutil -a /Volumes/Diskname

For example:

  [Computer:/] root# vsdbutil -c /Volumes/Disk
  Permissions on '/Volumes/Disk/' are enabled.

Permissions are on.

  [Computer:/] root# ls -l /Volumes/Disk
  ...
  -rw-r--r--    1 james     admin    994 Jul 12 13:39 getip.pl
  -rwxr-xr-x    1 rootadmin 433544 Jun 26 14:12 hping_fat
  drwxrwxr-x    3 unknown   unknown  102 May 12 11:45 iMacSMCUpdate.pkg
  -rwxr-xr-x    1 unknown   unknown 2374 Dec 28  2005 indexLoops
  drwxr-xr-x   30 james     admin   1020 Sep  5 11:18 modo
  drwxrwxrwx   52 unknown   unknown 1768 Sep 19 21:53 ruby
  drwxr-xr-x   10 unknown   unknown  340 Sep 19 21:53 textmate
  -rw-r--r--    1 unknown   unknown  983820084 Aug 15 15:47 xcode_2.4_8k1079_6936199.dmg

Notice the unknown user and group? That is because I created those files while permissions were off. I'll now turn off permissions, and see what havoc breaks loose.

  [Computer:/] root# vsdbutil -d /Volumes/Disk
  [Computer:/] root# vsdbutil -c /Volumes/Disk
  Permissions on '/Volumes/Disk/' are disabled.

Permissions are now off. Notice I'm the root user.

  [Computer:/] root# ls -l /Volumes/Disk
  -rw-r--r--    1 unknown  unknown  994 Jul 12 13:39 getip.pl
  -rwxr-xr-x    1 unknown  unknown     433544 Jun 26 14:12 hping_fat
  drwxrwxr-x    3 unknown  unknown  102 May 12 11:45 iMacSMCUpdate.pkg
  -rwxr-xr-x    1 unknown  unknown 2374 Dec 28  2005 indexLoops
  drwxr-xr-x   30 unknown  unknown 1020 Sep  5 11:18 modo
  drwxrwxrwx   52 unknown  unknown 1768 Sep 19 21:53 ruby
  drwxr-xr-x   10 unknown  unknown  340 Sep 19 21:53 textmate
  -rw-r--r--    1 unknown  unknown  983820084 Aug 15 15:47 xcode_2.4_8k1079_6936199.dmg

That's different. Now I'll become the user named "mac" (a local account on my computer) and look at the permissions.

  [Computer:/] root# su mac
  [Computer:/] mac% ls -l /Volumes/Disk
  -rw-r--r--    1 mac  mac  994 Jul 12 13:39 getip.pl
  -rwxr-xr-x    1 mac  mac     433544 Jun 26 14:12 hping_fat
  drwxrwxr-x    3 mac  mac  102 May 12 11:45 iMacSMCUpdate.pkg
  -rwxr-xr-x    1 mac  mac 2374 Dec 28  2005 indexLoops
  drwxr-xr-x   30 mac  mac 1020 Sep  5 11:18 modo
  drwxrwxrwx   52 mac  mac 1768 Sep 19 21:53 ruby
  drwxr-xr-x   10 mac  mac  340 Sep 19 21:53 textmate
  -rw-r--r--    1 mac  mac  983820084 Aug 15 15:47 xcode_2.4_8k1079_6936199.dmg

The user named "mac" owns all those files! Now I'll become a different user and see what happens.

  [Computer:/Volumes] mac% exit
  exit
  [Computer:/Volumes] root# su james
  [Computer:/Volumes] james% ls -l Radmind Ignores/
  -rw-r--r--    1 james  james  994 Jul 12 13:39 getip.pl
  -rwxr-xr-x    1 james  james     433544 Jun 26 14:12 hping_fat
  drwxrwxr-x    3 james  james  102 May 12 11:45 iMacSMCUpdate.pkg
  -rwxr-xr-x    1 james  james 2374 Dec 28  2005 indexLoops
  drwxr-xr-x   30 james  james 1020 Sep  5 11:18 modo
  drwxrwxrwx   52 james  james 1768 Sep 19 21:53 ruby
  drwxr-xr-x   10 james  james  340 Sep 19 21:53 textmate
  -rw-r--r--    1 james  james  983820084 Aug 15 15:47 xcode_2.4_8k1079_6936199.dmg

Now the user "james" owns the files!

What does this mean? If you allow other users SSH access to your computer and you think that they can't access mounted volumes, you better make sure by checking that permissions are not ignored. If permissions are ignored on a volume, every user that logs in will be the owner of everything on that volume.


Mount an AFP volume

Mounting a disk is an existential act. First, you must have a plain (but empty) folder. If you want, you can create a new one using mkdir. For example:

  [Computer:/Volumes] james% mkdir "/Volumes/A Very New Disk"
  [Computer:/Volumes] james% ls -l
  total 8
  drwxr-xr-x    2 james  admin     68 Oct  7 20:52 A Very New Disk
  lrwxr-xr-x    1 root   admin1 Sep 27 09:35 Macintosh HD -> /

It's there. But it's empty:

  [Computer:/Volumes] james% ls -l "/Volumes/A Very New Disk"
  [Computer:/Volumes] james%

Now you can proceed.

Mount a volume (so you don't have to type your password, which keeps the password out of the history file):

  mount_afp -i afp://user@server.example.com/Disk_Name "/Volumes/A Very New Disk"

Mount a volume with the password (for a script):

  mount_afp afp://user:password@server.example.com/Disk_Name /Volumes/A Very New Disk"

If the disk mounts and the Finder doesn't notice, try this to update the Finder:

  disktool -r

To unmount it:

  umount /Volumes/A Very New Disk

Very simple (yet very mystical).

Another example mounting the disk in your home directory:

  [Computer:~] james% mkdir Radical
  [Computer:~] james% mount_afp -i afp://secret@secret.example.com/A Disk Radical
  Password:
  mount_afp: the mount flags are 0000 the altflags are 0020

The disk name is "A Disk" but the mount point is "Radical". And even more interesting is that "Radical" is located in my home directory! After I did this, Finder hid "Radical" from me and instead put it in my sidebar (how helpful). Terminal did the correct thing and allowed me to cd into it and do what I wanted. I don't know why Finder would go through the trouble to hide it. After all, it is very unlikely that a user would accidentally mount something in their home folder.


Find disk usage

Terminal can tell you how much space you have left just like the Finder.

  [Computer:~] james% df -lh
  Filesystem     Size   Used  Avail Capacity  Mounted on
  /dev/disk0s2    74G    68G   5.8G    92%    /
  /dev/disk1s2    74G    68G   5.8G    92%    /Users/james

I happen to be using encrypted home folders, and wouldn't you know it, /Users/james is a mounted disk! It is kinda odd it is the same size as the hard disk though.

And ironically, no matter how big hard drives get, I always push my usage above 90%.

Display the amount of disk space a folder/file is taking up.

  du -hd0 <path>

It counts everything right then and there, so if you run du -hd0 /, you might want to go find something else to do.

  2.3G    /System/

Change the number after the d option to show more details. For example 3 shows the sizes of everything 3 levels deep:

  [Computer:/Applications] james% du -hd3 /Library/Application Support/Luxology/modo 201/Documentation
  8.0K    /Library/Application Support/Luxology/modo 201/Documentation/help/common/css
  56K    /Library/Application Support/Luxology/modo 201/Documentation/help/common/img
  4.0K    /Library/Application Support/Luxology/modo 201/Documentation/help/common/script
  68K    /Library/Application Support/Luxology/modo 201/Documentation/help/common
  525M    /Library/Application Support/Luxology/modo 201/Documentation/help/pages/clips
  18M    /Library/Application Support/Luxology/modo 201/Documentation/help/pages/imgs
  1.6G    /Library/Application Support/Luxology/modo 201/Documentation/help/pages/video
  2.1G    /Library/Application Support/Luxology/modo 201/Documentation/help/pages
  2.2G    /Library/Application Support/Luxology/modo 201/Documentation/help
  2.2G    /Library/Application Support/Luxology/modo 201/Documentation

Thus we see that "help/pages/video" is 1.6 GB and "help/pages/clips" is 525 MB, totaling 2.1 GB, the bulk of modo's documentation (each folder contains video files).


Creating an archive

This will create archivename.zip and will include every file you specify:

  zip archivename.zip file1 file2 etc

This will create archivename.zip of the contents of "folder":

  zip -r archivename.zip folder

Hey folks, you can use tar too:

  tar cfvz archivename.tgz file1 file2 etc

  tar cfvz archivename.tgz folder

You can double click on these in the Finder to open them (the tgz too). Or you can uncompress them from Terminal too:

  unzip archivename.zip

  tar xfvz archivename.tgz

A note about tar. It has many purposes, and creating tgz files isn't the main one, so that is why you have to type so much to get it to do it. Just remember c stands for compress, x for extract, and fvz all kinda make similar sounds (and are all typed with the right hand on a qwerty keyboard). O:)


Restart

  sudo reboot
  sudo shutdown -r now

You need to be root to restart a computer obviously.


Shutdown

  sudo shutdown -h now

Example:

  [remote-computer:~] root# shutdown -h now
  Shutdown NOW!
  shutdown: [pid 12792]
  [remote-computer:~] root#
  *** FINAL System shutdown message from mac@james-tech-mac-1.scl.utah.edu ***

  System going down IMMEDIATELY





  System shutdown time has arrived
  Stopping Network Information Service
  Stopping Apache web server
  Stopping network time synchronization
  /usr/sbin/apachectl stop: httpd (no pid file) not running
  Starting...
  Stopped ARD Helper.
  Stopped ARD Agent.
  Stopped VNC Server.
  Done.
  Connection to remote-computer.example.com closed by remote host.
  Connection to remote-computer.example.com closed.
  [Computer:/Volumes] root# ping 12.34.56.78
  PING 12.34.56.78 (12.34.56.78): 56 data bytes
  ^C
  --- 12.34.56.78 ping statistics ---
  10 packets transmitted, 0 packets received, 100% packet loss

Oops. Now what? Guess I'll have to wait until I get to work on Monday.


Logout

  killall loginwindow

Ok, that really isn't the real way to logout. But it IS quick and works.

Although this should work, it doesn't:

  osascript -e 'try' -e 'ignoring application responses' -e 'tell application "loginwindow" to &#171;event aevtrlgo&#187;' -e 'end ignoring' -e 'end try'

I'm assuming it doesn't work because it can't wrap its head around the chevron characters.

Determined not to be thwarted, I came up with this awkward solution. First, you have to create the script on your local machine. If you don't mind using Script Editor.app, save the following as logout.scpt:

  echo try
  ignoring application responses
  tell application "loginwindow" to &#171;event aevtrlgo&#187;
  end ignoring
  end try

Then run this command to get a hexdump of it:

  cat logout.scpt | perl -ne 'print unpack "H*", $_'

Mine prints out this:

  4661736455415320312e3130312e31300e000000040fffff00010002000301ff
  ff00000d000100026c00020000001c0004fffe0d000400035100000000001c00
  050006fffd0d00050003500000000300130007fffc00080d000700024f000100
  0800120009000a0d00090003490002000c0011fffbfffafff90afffb00182e61
  657674726c676f2a2a2a2a00000000000090002a2a2a2a01fffa000002fff900
  000d000a00016d000000080009000b0f000b01d8086e756c6c000000000001df
  80ff9c000975b30f6c6f67696e77696e646f772e617070db3016488280000000
  800000000100000001a001021890045ff8bfffd9d0bfffdbe190046044991e50
  d8bfffd9d0bfff6c676e7700001100616c697300000000017a00020001084d61
  63204f53205800000000000000000000000000000000000000bf145f5d482b00
  00000975b30f6c6f67696e77696e646f772e6170700000000000000000000000
  0000000000000000000000000000000000000000000000000000000000000000
  00000000000009991abf140d330000000000000000ffffffff00000920000000
  0000000000000000000000000c436f72655365727669636573001000080000bf
  14b3bd0000001100080000bf14619300000001000c000975b300090bf500090b
  f3000200344d6163204f5320583a53797374656d3a4c6962726172793a436f72
  6553657276696365733a6c6f67696e77696e646f772e617070000e0020000f00
  6c006f00670069006e00770069006e0064006f0077002e006100700070000f00
  120008004d006100630020004f0053002000580012002b53797374656d2f4c69
  62726172792f436f726553657276696365732f6c6f67696e77696e646f772e61
  707000001300012f00ffff000002fffc00000200080002fff8fff70afff80008
  0b636f6e73726d746502fff700000d0006000352000000000000fff6fff5fff4
  0afff600182e61736372657272202a2a2a2a00000000000090002a2a2a2a01ff
  f5000002fff4000001fffd000001fffe00000e000200000f1000030003fff300
  0c000d01fff3000010000c0001fff20afff200182e616576746f6170706e756c
  6c00008000000090002a2a2a2a0e000d000710fff1000efff0ffef000f0010ff
  ee0afff100182e616576746f6170706e756c6c00008000000090002a2a2a2a0d
  000e00016b00000000001c001102001100020001ffed02ffed000001fff00000
  02ffef000010000f000010001000050008000bffecffebffea0affec00182e61
  657674726c676f2a2a2a2a00000000000090002a2a2a2a01ffeb000002ffea00
  0011ffee001d14001567e013000de11200072a6a0c0002555657000858000300
  04680f00617363720001000dfadedead

Copy that. Then on the remote machine, type "pico logout.hex" (see below for more info on pico). Paste. Type ctrl-x to save the file and exit pico. Then type this:

  perl -e 'chomp ( $hex = `cat logout.hex`);' -e 'print pack "H*", $hex' > logout.scpt

Then to logout, type

  osascript logout.scpt

This will not ask to logout. However, if an app refuses to quit, logout will be canceled. So if after, say, 30 seconds you haven't logged out, run top, look to see what is still running, and you can kill it or you can actually tell it to quit with this command:

  osascript -e 'tell application "Terminal" to quit'

If it doesn't respond to that, perhaps it has a sheet asking for user input. I suppose you could use UI scripting to click "OK" or whatever, but even I have to say that is excessive.

However, what if you aren't sure what is going on? If the screensaver on the remote machine isn't running, you can see what the screen looks like with this command:

  screencapture filename.png

Then scp that file to your machine and voilĂ , poor man's VNC (except VNC is free). It is a neat trick anyway.

An even neater trick is if you have an isight, and you think someone is using your computer. Use isightcapture to take a picture of them (You'll have to download this).


Download a file

  curl -O http://example.com/filename

If you install wget then you can use it as well (Rudix is probably the easiest way to get it for Mac OS X). Example:

  wget http://example.com/filename


Sleep

  osascript -e 'tell application "System Events"' -e 'sleep' -e 'end tell'

Remotely waking it is going to be a bit hard. Apple Remote Desktop can do it (if you are on the same subnet). Otherwise, good luck.


Finding out what is running

So the dock shows you what is running. You can get the same information by running the ps command with the Aww flags, which tells ps to show all processes and their full path.

  [Computer:~] james% ps -Aww
  PID  TT  STATTIME COMMAND
  1  ??  S<s    0:02.99 /sbin/launchd
  23  ??  Ss     0:00.95 /sbin/dynamic_pager -F /private/var/vm/swapfile
  27  ??  Ss     0:01.66 kextd
  31  ??  Ss     0:28.50 /System/Library/PrivateFrameworks/DedicatedNetworkBuilds.framework/Resources/bfobserver
  32  ??  Ss     0:00.06 /usr/sbin/KernelEventAgent
  33  ??  Ss     0:05.80 /usr/sbin/mDNSResponder -launchdaemon
  34  ??  Ss     0:05.17 /usr/sbin/netinfod -s local
  35  ??  Ss     0:00.87 /usr/sbin/syslogd
  37  ??  Ss     0:31.53 /usr/sbin/configd
  38  ??  Ss     0:03.42 /usr/sbin/coreaudiod
  ...

This will show you lots of good information, like the process ID (PID). Using the PID, you can kill (force quit) a process (see below). The STAT column tells you what the process is doing, such as sleeping (good processes are very much like cats, they sleep an awful lot). It also tells how much time of the CPU the process has consumed. Good, bug free processes, will not use the CPU when you are not using it. As of this writing, Photoshop is still Carbon, so we still must forgive it.

To read the STAT column, look at the first character. It will be either I (idle), S (sleep), R (runnable), T (stopped), U (uninterruptible wait, waiting for some disk or network event-possibly hung, but not necessarily), or Z (zombie). If you see a Z, then you have a zombie process that is trolling your computer eating other processes. Just kidding. A zombie process is a process that was quit but it wont die. To get rid of it, you will have to restart the computer. Zombie processes aren't suppose to happen. I've actually had some computers not restart because the computer waits for the zombie to die, and it never does, so I had to hard restart it. I've also had some computers with zombies not boot up after restarting. So having a zombie could spell trouble. Then again, it may be nothing but a programmer bug.

Adding the j flag will show user type information:

  [Computer:~] james% ps -Awwj
  USER PID  PPID  PGID   SESS JOBC STAT  TT TIME COMMAND
  root   1     0     1 2844e88    0 S<s   ??    0:03.00 /sbin/launchd
  root  23     1    23 2844c60    0 Ss    ??    0:00.95 /sbin/dynamic_pager -F /private/var/vm/swapfile
  root  27     1    27 2844b4c    0 Ss    ??    0:01.66 kextd
  root  31     1    31 28438f8    0 Ss    ??    0:28.51 /System/Library/PrivateFrameworks/DedicatedNetworkBuilds.framework/Resources/bfobserver
  root  32     1    32 28445e8    0 Ss    ??    0:00.06 /usr/sbin/KernelEventAgent
  ...
  windowse    56     1    56 28437e4    0 Rs    ??   41:57.92 /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/WindowServer -daemon
  root  61    37    37 28444d4    0 S     ??    0:02.16 /usr/sbin/blued
  root  64     1    64 2843a0c    0 Ss    ??    4:41.96 /System/Library/CoreServices/coreservicesd
  james 69     1    69 28436d0    0 Ss    ??    0:09.11 /System/Library/Frameworks/ApplicationServices.framework/Frameworks/ATS.framework/Support/ATSServer
  james 70     1    70 28434a8    0 Ss    ??    0:05.07 /System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow console
  ...

Another method to find out what is running on your system is to use top. It will show you a second by second snapshot of what is running, so you can get a vague idea how busy your machine is.

  Processes:  72 total, 2 running, 70 sleeping... 269 threads16:18:21
  Load Avg:  0.43, 0.31, 0.30     CPU usage:  7.6% user, 8.0% sys, 84.4% idle
  SharedLibs: num =  241, resident = 36.6M code, 5.25M data, 5.83M LinkEdit
  MemRegions: num = 10821, resident =  305M + 15.9M private,  153M shared
  PhysMem:   233M wired,  473M active,  277M inactive,  984M used, 39.1M free
  VM: 12.3G +  158M   330051(0) pageins, 265393(0) pageouts

  PID COMMAND%CPU   TIME   #TH #PRTS #MREGS RPRVT  RSHRD  RSIZE  VSIZE
  5649 top   10.6%  0:08.43   1    18    20   624K   692K  1.04M  26.9M
  5618 tcsh   0.0%  0:00.02   1    15    20   408K   976K   908K  31.1M
  5600 tcsh   0.0%  0:00.04   1    15    20   408K   976K   912K  31.1M
  5548 mdimport     0.0%  0:00.15   3    61    42   704K  2.81M  2.34M  38.9M
  5522 mdimport     0.0%  0:00.57   4    66   106  1.29M  2.92M  3.66M  57.9M
  5369 ping   0.0%  0:01.54   1    14    19   140K   648K   376K  26.8M
  5355 iChatAgent   0.0%  0:00.52   3    70    68  1.34M  3.53M  11.6M   297M
  5354 iChat  0.0%  0:01.13   6   243   252  7.25M  9.54M  31.3M   360M
  ...


Force quitting processes

You can force quit ("kill") a process using the pid (obtained with ps or top):

  sudo kill 1

Or you can kill it by name:

  sudo killall launchd

Or you can force kill it (SIGKILL) if polite kill (SIGTERM) doesn't work:

  sudo kill -9 1
  sudo killall -9 launchd

(BTW, killing launchd is a very bad idea.)


Editing text files from the command line

The easiest option to edit via command line is pico (nano in 10.4). It was written for non-programmers to use with the email reader pine. So pico shows you the available commands at the bottom of the terminal window and is fairly obvious (for a Terminal based editor).

A problem with pico is that it will hard wrap lines. nano does not have this problem. However, if resize the window bigger while using nano it will hang (and take as much CPU as it can get away with). It does this in iTerm too, so it isn't a Terminal.app bug.

  pico <filename>
  nano <filename>

In 10.4, if you type pico, it will open nano for you. For some reason, I can't get out of the habit of typing pico.

If you type pico and then the name of a file that doesn't exist, pico will create that file when you save. There is no way to open a new file (like the "File" -> "Open" menu). You have to exit and then type pico again with a new filename.

The important functions in pico are:

  ctrl-o to save (it means write out, out being the significant word... don't ask me why)
  ctrl-x to exit (it will ask if you want to save if you made changes and haven't saved)
  ctrl-w to find (where being the significant word)
  ctrl-y page up
  ctrl-v page down
  ctrl-k cut line(s)
  ctrl-u uncut line(s)
  ctrl-shift-6 start/stop mark (so you can cut many lines)

If you ever press ctrl-j (which is dangerously close to ctrl-k), it will "wrap" everything for you and I think it does a pretty miserable job, especially if it is code. Just press ctrl-u right after to undo it.


Hardcore editing text files from the command line

If you are serious about being a hardcore command line person, you will want to learn vi or emacs. I'll just say that you should at least learn enough vi to change, save, and exit a file, because visudo is one command line utility that forces you to use vi. Ok, you can configure it to use pico, but learning vi that hard. Well, if you really want to use pico, check out the next section (set EDITOR to "/usr/bin/pico").

Edit a file with vi:

  vi <filename>


Environment Variables

I remember when I first learned Unix there were these mysterious things called environment variables. I knew what a variable was from algebra (y=m*x anyone?), what why was it called environment? If you are into 3D graphics, you might think it has something to do with the background lighting. Well, as it turns out, the environment isn't that complex. If you open 2 terminal windows, and you change a variable in one, it wont affect the other window. That is because each has its own "environment" that is independent of each other.

What sort of variables are there? Well, anything you want. There are many built in variables, like your HOME, your PATH, and your EDITOR. There variables are set to the locations of your home folder and the paths of terminal commands.

This is how to change a variable if you are using BASH:

  export VAR="new value"

And now TCSH:

  setenv VAR "new value"

This is how to change the PATH variable in BASH (specify $PATH to keep the old values, which is important):

  export PATH=/usr/local/mysql/bin:$PATH

And now TCSH:

  setenv PATH /usr/local/mysql/bin:$PATH

If you want a variable to affect other terminal windows, you need to make it part of the shell startup sequence. You do that by editing the startup script for the shell. The following commands will add the text you need for you (very handy):

BASH:

  echo 'export PATH=/usr/local/mysql/bin:$PATH' >> ~/.bash_profile

TCSH:

  echo 'setenv PATH /usr/local/mysql/bin:$PATH' >> ~/.tcshrc


Replacing BBEdit's Multi-file search with the command line

To search for a word or phrase in a file or multiple files use grep. Grep stands for "global regular expression print", which is a function of the "ed" command: g/re/p" where g is global, re represents the regular expression, and p is print. The name doesn't come from the sound cats make coughing up hairballs (which is more like "grk").

A regular expression is a formula for matching text that fits a pattern. The most obvious match is equals, that is "text" equals "text". With grep, you cam perform complex system of wildcards matches, like "(T|t).x(T|t)" matches "text" (and many other combinations).

The really important thing to know is that certain characters have special regular expression meanings. When you use the special character, magical things will happen, and you suddenly find all kinds of text! For example, ".*" is a pattern that will find EVERYTHING! Another example is "..." which will find everything that has 3 characters!

So, this brings up a point. What if all you want is to find 3 real periods? You need to tell grep to not treat it like a special character, but as a character to find. Do this by "escaping" the character by placing a backslash in front of it, like this ".".

Here are the characters with regular expression meanings:

.[]?*+{}|

^ and $ also have special meanings if they come at the beginning or end of a word.

Any character following a also has special meanings (like "d" means digit), except for the special characters of course. Thus "." means period, "[" means "[", "?" means "?", etc.

Ironically, grep requires -e in order to use full regular expressions. You can also use egrep, which is the same thing as grep -e.

Here is an example of looking for errors in the system.log file (using the case insensitive switch):

  [Computer:/var/log] james% grep -i error system.log
  Oct  7 14:41:53 Computer cp: error processing extended attributes: Operation not permitted
  Oct  7 16:48:37 Computer kernel[0]: IOAudioStream[0x2b5df00]::clipIfNecessary() - Error: attempting to clip to a position more than one buffer ahead of last clip position (0,a83)->(1,1d5e).

-i specifies that the search is case insensitive (default is case sensitive).

Here is an example of searching for the text "NSWindow" recursively in the Apple Developer Examples but only in files that end with ".m".

  [Computer:/Developer/Examples] james% grep -r --include="*.m" NSWindow *
  Accessibility/AXCanvas/CanvasDoc.m:- (void)windowControllerDidLoadNib:(NSWindowController *)aController
  Accessibility/AXCanvas/CanvasInspectorController.m:#pragma mark NSWindowController methods
  Accessibility/AXCanvas/CanvasProxyTabView.m:- (NSWindow *)caxWindowAttribute {
  AppKit/ClockControl/ClockControl.m:    [notifCenter addObserver:self selector:callback name:NSWindowDidBecomeKeyNotification object: [self window]];
  AppKit/ClockControl/ClockControl.m:    [notifCenter addObserver:self selector:callback name:NSWindowDidResignKeyNotification object: [self window]];
  ...

The following example searches for NSWindow like before, but omits NSWindowController. It does this by taking the output of the first grep and "piping" it to the grep -v, which will remove lines with NSWindowController (see below for more about the pipe "|").

  [Computer:/Developer/Examples] james% grep -r --include="*.m" NSWindow * | grep -v NSWindowController
  Accessibility/AXCanvas/CanvasProxyTabView.m:- (NSWindow *)caxWindowAttribute {
  AppKit/ClockControl/ClockControl.m:    [notifCenter addObserver:self selector:callback name:NSWindowDidBecomeKeyNotification object: [self window]];
  AppKit/ClockControl/ClockControl.m:    [notifCenter addObserver:self selector:callback name:NSWindowDidResignKeyNotification object: [self window]];
  ...

This version uses the -w option to specify only the word NSWindow.

  [Computer:/Developer/Examples] james% grep -r -w --include="*.m" NSWindow *
  Accessibility/AXCanvas/CanvasProxyTabView.m:- (NSWindow *)caxWindowAttribute {
  AppKit/HexInputServer/HexInputContext.m:    NSWindow *window = [textField window];
  AppKit/Sketch/SKTGridPanelController.m:- (void)setMainWindow:(NSWindow *)mainWindow {
  AppKit/Sketch/SKTInspectorController.m:- (void)setMainWindow:(NSWindow *)mainWindow {
  AppKit/TextEdit/Controller.m:  NSWindow *window = [windows objectAtIndex:count];
  AppKit/TextEdit/Document.m:    NSWindow *window = [scrollView window];
  AppKit/TextEdit/Document.m:  if (same && updateIcon) [[self window] setTitleWithRepresentedFilename:@""]; // Workaround NSWindow optimization
  AppKit/TextEdit/Document.m:- (NSWindow *)window {

This next grep uses extended grep (egrep, which is the same as grep -e) to display files that have NSWindow or NSPanel.

  [Computer:/Developer/Examples] james% egrep -r -w --include="*.m" "NS(Window|Panel)" *
  Accessibility/AXCanvas/CanvasProxyTabView.m:- (NSWindow *)caxWindowAttribute {
  AppKit/HexInputServer/HexInputContext.m:    NSWindow *window = [textField window];
  AppKit/HexInputServer/HexInputServer.m:  NSPanel *panel = [[NSPanel allocWithZone:[self zone]] initWithContentRect:NSMakeRect(0, 0, 100, 40) styleMask:NSBorderlessWindowMask|NSUtilityWindowMask backing:NSBackingStoreBuffered defer:YES];
  AppKit/Java/TextEdit/TextFinder.m:- (NSPanel *)findPanel {
  AppKit/Java/TextEdit/TextFinder.m:    return (NSPanel *)[findTextField window];
  AppKit/Java/TextEdit/TextFinder.m:    NSPanel *panel = [self findPanel];
  AppKit/Sketch/SKTGridPanelController.m:- (void)setMainWindow:(NSWindow *)mainWindow {


Piping

Piping is when the output of one command is sent to another. So far, the output of every command we have tried so far is just printed to the terminal. By using the pipe character, "|", we can send that output to commands that take input. Here is an example of where that is useful.

A very simple case of piping is when we are looking to see if a particular process is running.

  ps -Aww | grep -i <process name>

For example:

  [Computer:~] james% ps -Aww | grep -i Safari
  2121  ??  S     21:39.47 /Applications/Safari.app/Contents/MacOS/Safari -psn_0_18612225
  5669  p1  R+     0:00.00 grep -i Safari

Technically, there are 2 processes running with "Safari" as part of the command. One is Safari of course, the other is the very grep command! That is because when ps was running finding processes, so was grep. So ps showed us grep and grep showed itself because it had Safari as part of its command. Kinda like chicken and egg. Anyway, it will always be listed in the process listing, just like top will be when running top.

To remove it grep from the output, use grep -v grep. The "-v" flag says to exclude a pattern. So we are taking the output of the ps, and print anything that has "Safari", then we take that output and print everything unless it has "grep". Like this:

  ps -Aww | grep -i <process name> | grep -v grep

Which produces this:

  [Computer:~] james% ps -Aww | grep -i Safari | grep -v grep
  2121  ??  S     21:53.69 /Applications/Safari.app/Contents/MacOS/Safari -psn_0_18612225

That's better.


A radmind grep example

If you aren't a radmind administrator, skip this section. So lapply gave an error that said that the folder "/Library/Application Support/Adobe/StartupScripts" could not be deleted because it was not empty. That means Radmind doesn't think the folder should exist, which happens if no transcripts contain the folder. Naturally, it would have contents if Radmind is told to put files in the folder. Radmind is like that, it wont create folders for you, even if you tell it to put files in the folder.

So we want to find out what transcripts specify files in that location, and we want to double check that at least one of them creates the folder (all of them should really). To do that, we search the files in "/var/radmind/client" for "/Library/Application Support/Adobe/StartupScripts".

First, we need to encode the path in such a way that grep can actually find it. Replace the space with "" because that is how radmind transcripts encode spaces. Escape the backslash "" in "" since it is a grep special character, so it becomes "". That results in "/Library/ApplicationSupport/Adobe/StartupScripts". We will put quotes around it because the shell interprets "" also. By putting quotes around it, you are telling shell just give it to grep unaltered.

So recursively search for the text and only print out transcript filenames:

  grep -lr "/Library/ApplicationSupport/Adobe/StartupScripts" /var/radmind/client

If you run this command, you will see all the transcripts that specify files in that location. Now we want to find out if one of them actually creates the folder. Do this by leaving out the "-l" flag so grep prints out every text match, like this:

  grep -r "/Library/ApplicationSupport/Adobe/StartupScripts" /var/radmind/client

But then exclude everything that has the folder separator character after the word "StartupScripts", like this:

  grep -v "StartupScripts/"

The line that creates the folder wont have the separator character after the name, so this should only print lines creating the folder:

  grep -r "/Library/ApplicationSupport/Adobe/StartupScripts" /var/radmind/client | grep -v "StartupScripts/"

Considering we got the error we did, we probably wont see anything! That is how we would know it isn't specified in any transcript! This is an easy mistake to make when updating overloads. To fix it, go to the server and execute the first grep (except search in "/var/radmind/transcript"), and add the directory to one or all of the transcripts.


What next?

I've pretty much covered everything the Finder can do, except burning CD's, and I've never done that from the command line, and I don't really feel like trying to figure it out (my ISP is currently down, and well, I didn't know all of this stuff off the top of my head ya know). And I'm not sure if it is possible from the command line to set the "Open with..." attribute of a file. I'm not sure that is possible.

So there you are! Who says we need a new Finder? You no longer need to use it!

Where to go next? How about learn everything there is to know about commands!

Copyright 2017 James Reynolds