Professional Documents
Culture Documents
To search for a specific file use the 'find' command. 'find' does a live search on the file
system, unlike 'locate' which searches a database.
Find all files in your home directory called 'my_letter':
find ~ -name my_letter
To search the current directory instead, replace '~' with '.'. To search the entire file system
use '/'.
find a group of files
To search for a group of files with a common prefix or ending, use 'find' with wildcards in the
filename. Enclose the name in quotes.
Find all files with extension text in the current directory:
find . -name "*.txt"
Find all files starting with 'temp' searching the whole file system:
find / -name "temp*"
find big and small files
The 'find' command can be used to search for files equal to, greater than, or less than a given
size. The size is given as the number of 512 byte blocks. If preceded with '+' this means greater
than the given size, '-' means less than.
To find all files in your home directory bigger than 1 Meg use:
find ~ -size +2048
The size 2048 is obtained from: 1 Meg divided by the block size of 512.
To find all empty files in the current directory use:
find . -size -1
man basics
If you are not sure how to use a Unix command (such as 'ls') read its manual page using:
man ls
And to find out how to use 'man' itself...
man man
The manual contains other useful information besides commands - try these:
man hier
man ascii
search by keyword
Unix_tricks.txt Page 5 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
If you need to do something but cannot think of a Unix command that might help, locate a suitable
command by keyword lookup. For example, commands that may change the owner of a file can be
located with either:
man -k owner
or
apropos owner
the 8 man sections
The Unix manual is divided into 8 sections:
* (1) user commands
* (8) system commands
* (5) configuration Files
less useful:
* (6) terminal games - empty :-(
* (7) miscellaneous
* (2,3,4) library calls for programmers
Find out about each section with:
man <section number> intro
Unfortunately, 'intro' is lacking detail in OS X. Instead, list section commands with (for
section 1):
man -k "(1)"
'files' and 'see also'
When reading a manual page, look at the 'FILES' and 'SEE ALSO' sections at the end. 'FILES' lists
configuration files associated with the command. 'SEE ALSO' lists similar commands and is a good
way to learn more Unix commands.
Often, information about the format of a file listed in 'FILES' can be got by:
man name-of-file
or
man 5 name-of-file
the man pager
The environment variable 'PAGER' tells 'man' the command it should use to display its output. The
default is 'more'. Read the manual page for 'more' in order to find out how to better control the
output from 'man'.
If you wish to use a different pager, such as 'less', set the environment variable with:
setenv PAGER less
To get 'man' to write its output to a file, and also remove the formatting, type:
man the-command | col -b > a-file
Unix_tricks.txt Page 6 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
star and query
So what is globbing? Globbing is where you specify an ambiguous filename and the shell finds all
those files (in the given directory) that match it.
The globbing character '*' tells the shell to match 0 or more characters - any character will
match.
rm *
will remove ALL files in the current directory. Actually, that's not quite true. A leading '.'
must be matched explicitly, so filenames that begin with dot must be removed with 'rm .*'.
rm *.txt
will remove all files with the extension '.txt'
The character '?' tells the shell to match any one character.
rm test?
will remove 'test1', 'testa', and 'test$'. It will not remove 'test' or 'test12'.
selected characters
Enclose characters in '[ ]' to tell the shell to match any one of a specific list of characters.
ls b[aeiou]g
will list 'bag', 'beg', 'big', 'bog', and 'bug'; but not 'bfg' or 'bags'.
The shell processes matched filenames in alphanumeric order.
ranges
To match any one of a range of characters, separate a pair of characters with '-'.
[a-z] matches any lowercase letter.
[0-9] matches any digit.
[a-z0-9] matches any lowercase letter or digit.
glob patterns may be combined, as in:
ls [mt]*day[0-9]
Match filenames that start with 'm' or 't', have any number of intervening characters, and end in
'day' followed by one digit.
escaping
If you wish to pass any of the characters * ? [ ] to a command, you must prevent the shell from
globbing them first by escaping those characters. Use either quotes or backslash.
To pass '*' to the 'find' command, use:
find . -name "*.html"
find . -name '*.html'
find . -name \*.html
Unix_tricks.txt Page 7 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Better than 'more' is 'less'. To view a file with less:
less todo.txt
The commands are as for 'more', plus many others. Most of the navigation commands from the text
editor 'vi' will work too.
Use the cursor (arrow) keys to move back and forth through a file, and to view a wide file.
Press:
control-g to display file status information
ng to go to line n
G to go to the end of the file
n% to position n% into the file
/pattern to search forward for 'pattern'
?pattern to search backwards for 'pattern'
n to repeat the last search
All occurrences of a match pattern are highlighted.
Place bookmarks and return to them at any time (exactly as for 'vi') with:
ma to mz to place a bookmark
'a to 'z to return to a bookmark
heads or tails
To view the first few lines of a file use 'head'.
head todo.txt
To view the last few lines of a file use 'tail':
tail todo.txt
To view the first or last 'n' lines of a file:
head -n 20 todo.txt
tail -n 15 todo.txt
chase your tail
To view a file live (as it is updating) use option '-f' with 'tail'.
tail -f todo.txt
will print the last few lines of 'todo.txt', then monitor the file printing out additional lines
as and when they are added. This is useful to monitor progress and log files.
The command:
tail -f /var/tmp/console.log
will watch the console log. The application Console in Utilities is simply a GUI front-end to
this command.
Press 'control-c' to quit 'tail'.
directory sizes
Unix_tricks.txt Page 9 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
These tricks apply equally to 'disks' and 'discs' :-)
To report on how much disc space a directory occupies, use the disc usage command 'du'. To query
your home directory for example:
du ~
This will display the size of every directory from your home directory down. The size of a
directory includes the size of all directories within it. The last line displayed will be the
total size of your home directory.
The size is reported in file-system blocks. Each block is 512 bytes or 1/2k. Take the answer you
get and divide by 2 to get the size in k-bytes.
summary of directory sizes
To report on how much disc space a directory occupies, but without displaying that information
for every sub-directory, use the '-s' or summary option.
du -sk ~
Will print a single line displaying the total size of your home directory, and in k-bytes too
(requested by the '-k' option).
Specifying option '-x' tells 'du' to avoid mounted file systems.
summary of disc usage
To display how full each disc and partition is, use the 'df' commnd:
df -k
Option '-k' causes sizes to be displayed in k-bytes.
tar balls
A 'tar ball' is Unix-speak for a single file into which many other files have been bundled, or
archived - similar to an Aladdin 'stuffit' file or a disc image.
To archive 'Documents' use:
tar cvf Documents.tar Documents
'Documents' remains untouched. Documents.tar is created and is a single file that contains a copy
of all the directories and files in Documents.
To list the contents of an archive:
tar -tf Documents.tar
To expand an archive and reconstruct the original directory:
tar -xvf Documents.tar
zipping up files
To compress a file use 'gzip'. This is the GNU implementation of the Lempel-Ziv coding used in
the 'zip' family of commands.
To compress the archive we created on Thursday:
gzip Documents.tar
Unix_tricks.txt Page 10 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
This will overwrite Documents.tar with Documents.tar.gz.
Documents.tar.gz is very much like an Aladdin stuffit archive. A single compressed file that can
be decompressed and expanded.
To decompress use:
gunzip Documents.tar.gz
This will overwrite Documents.tar.gz with Documents.tar.
Sometimes gunzip will give an error message something like:
'...more than one file...'
In this case try using 'unzip'.
'tar' can archive and compress, or decompress and expand, in one go. Use the 'z' option to get
'tar' to zip and unzip.
tar cvfz test.tgz test
tar xvfz test.tgz
Unix Calendar
To display a calendar for any month or year use the 'cal' command.
This month:
cal
This year:
cal -y
April 1900:
cal 4 1900
Year 2112:
cal 2112
Unix Calculator
To perform complex calculations use the calculator 'bc'. The results from 'bc' are shown in
italic in the following examples.
2^64
18446744073709551616
365*24*60*60
31536000
42*(6+9)
630
scale=5
1/3
.33333
scale=100
22/7
3.14285714285714285714285714285714\
Unix_tricks.txt Page 11 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
2857142857142857142857142857142857\
1428571428571428571428571428571428
a=20
b=60
a*b
1200
++a*++b
1281
And this is just the tip of the 'bc' iceberg.
Unit Conversions
To find out how to convert from anything to anything use the Unix command 'units'. In the first
example I wish to convert between feet and metres (meters), and so I type 'feet' then 'metres'
(or 'meters').
units
You have: feet
You want: metres
* 0.3048
/ 3.2808399
You have: kilo-litres
You want: gallons
* 264.17205
/ 0.0037854118
You have: zetta-ergs
You want: joules
* 1e+14
/ 1e-14
You have: scruple
You want: grams
* 1.2959782
/ 0.77161792
control-c to exit.
Create Banners
Create a short banner with the Unix command 'banner'.
banner hello
# # ###### # # ####
# # # # # # #
###### ##### # # # #
# # # # # # #
# # # # # # #
# # ###### ###### ###### ####
Unix_tricks.txt Page 12 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Quiz Time
Today, a quiz.
How many days were there in September 1752? And why?
How many days into 2002 are we today?
How many pico-gallons in a bushel?
Copy to the Clipboard
Copy the output of a Unix command to the GUI clipboard (that to which one cuts, copies, and
pastes).
On the command line type (for example):
ls -al | pbcopy
or
cal | pbcopy
In a GUI app such as TextEdit select menu Edit->Paste, or command-v, to paste the output copied
from the Unix command.
August 2002
S M Tu W Th F S
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Paste from the Clipboard
Copy from the GUI clipboard into a Unix command.
Copy a page from TextEdit using command-c. Now paste it to the Unix command 'wc' ('wc' counts the
number of words in its input).
pbpaste | wc -w
348
Note that Mac GUI programs and Unix programs use a different end of line character, so the
clipboard will appear to contain one long line.
Drag and Drop
Drag and Drop onto the command line.
Change your current working directory to a folder you can see in the Finder without typing the
full pathname. Simply type:
cd <space>
Now select the destination folder and drag and drop in anywhere on the Terminal window, then
Unix_tricks.txt Page 13 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
press return.
This works for files as well as folders.
Launch GUI Applications
Launch a GUI application, or open a document, from the command line.
To launch an application use 'open -a'.
open -a /Applications/Clock.app/Contents/MacOS/Clock
This can also be done remotely via ssh, as long as the 'ssh' user and the user logged into GUI on
the remote machine are the same. Guaranteed to surprise the user of the remote machine :-)
To open documents, for example all text files in ~/Documents, type:
cd ~/Documents
ls *.txt
test.txt test2.txt
open *.txt
Both files will open in the default application for '.txt' - probably TextEdit.
Double Click a Shell Script
Run a shell script by double-clicking it in the GUI.
Create a shell script as usual, and give it the extension '.command'. Simple as that. Now
double-click it and a Terminal window will open, which automatically runs the script.
Remember that the command must be executable. Use:
chmod +x script-name.command
Terminal Startup
When you start the Terminal, or open a new Terminal window, the new shell executes commands from
(among others) ~/.tcshrc.
To display a friendly and personalised greeting on startup, add the following line to '.tcshrc'
in your home directory.
echo Hello $user
Use an editor such as pico to create this file, or use the following command:
echo 'echo Hello $user' >> ~/.tcshrc
Also, have a look at /etc/motd.
Customised Prompt
Change the Terminal prompt.
set prompt="%B%c4%b %d %D %w %t %n@%M\n%# "
This gives a two-line prompt:
~/SitesTutorials Wed 21 Aug 5:37pm
%
Unix_tricks.txt Page 14 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
ls LearningCanter
ls: LearningCanter: No such file or directory
set correct=all
ls LearningCanter
OK? ls LearningCenter? yes
AdvancedUnix UnixTutorials ...
Let's do the grep
Sherlock allows one to search the contents of files for a specific string. One can do this on the
command line too.
Use 'grep' to find all files in you home directory containing a given string.
grep -nis the-string-you-wish-to-search-for *
The option 'n' prints a line number, 'i' ignores case, and 's' suppresses warnings.
grep = Get Regular ExPression.
Let's do the Recursive grep
One can search a whole directory structure for a given string using recursive 'grep'
Use 'grep' to find all files in you home directory, and all directories within, by specifying
option '-r'.
grep -nisr the-string-you-wish-to-search-for *
Searching for Words
'grep' can be made to search for a whole word, rather than text that is contained within a word,
by specifying option '-w'.
For example:
grep -nisw day *
will find matches for 'day' as a whole word, but not 'day' as in 'today' or 'Wednesday'.
Searching for Patterns
Use grep to search files for text that matches a pattern rather than a fixed string.
To search for Saturday or Sunday but no other days use:
egrep -nis "(Satur|Sun)day" *
Note that we must use 'egrep', which is an extended version of grep, for this particular example.
If in doubt use egrep all the time.
(e)grep can do all sorts of fancy pattern matching, using what are termed 'regular expressions'
These are quite powerful and beyond the scope of this Trick, but will be covered in depth in the
Learning Center. If you speak 'vi' then you can use (mostly) the same regular expressions in
Unix_tricks.txt Page 16 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
(e)grep.
Word Counting
Count the number of words (and characters and lines) in a document. The document should be plain
text.
wc letter.txt
16 138 882 letter.txt
This letter has 16 lines, 138 words, and 882 characters.
Substitute with sed
If you wish to replace a particular string with another throughout a file use 'sed'.
To replace the occurrence of 'colour' with 'color' from 'old.html' and write the output to
'new.html' use:
sed 's/colour/color/g' old.html > new.html
's' stands for substitute. The 'g' at the end of the substitute pattern says to substitute all
occurrences of colour on each line, not just the first.
To write the processed file back to the original file use:
sed 's/colour/color/g' file.html > tmp; mv tmp file.html
sed = Stream EDitor
Strip with sed
Use 'sed' to strip out unwanted lines in files.
To remove lines containing the text DELETE-ME:
sed '/DELETE-ME/d' x > x.new
To remove all blank lines:
sed '/^$/d' x > x.new
Strip with grep
Use 'grep' to strip lines from files.
By specifying option '-v' 'grep' will print all lines that don't match the specified expression.
Therefore to strip all lines that contain 'rem' use:
grep -vw "rem" x > x.new
Use option '-w' to match 'rem' as a whole word.
Translate with tr
If you have a file with Mac style end of line character <CR> that you want to look sensible in a
Unix editor, you must translate all occurrences of <CR> to the Unix end of line <NL>.
Use:
Unix_tricks.txt Page 17 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
date
'date' can also be used to set the date and time with:
sudo date 200208222355.08
2002 August 22 23hrs 55mins 8 seconds
This command must be run as superuser and has the format: yyyymmddhhmm.ss
You may omit as much of the leading section as you wish.
The command 'time' does not display or set the time, it times execution of the command that
follows it.
Teleporting
Shell aliases are not the same as Finder aliases. A shell alias is shorthand for a command.
If you have a directory to which you often refer, make an alias of it.
alias cdut cd ~/Sites/Tutorials/LearningCenter/UnixTutorials
Now type:
cdut
to change directory to ~/Sites/Tutorials/LearningCenter/UnixTutorials
View Aliases
To view the aliases currently set type:
alias
To view a particular alias type:
alias alias-name-here
To remove an alias type:
unalias alias-name-here
When you create an alias, make sure it does not have the same name as an existing command with:
which your-new-name
Useful Aliases
When you start the Terminal, or open a new Terminal window, the new shell executes commands from
(among others) ~/.tcshrc.
To have aliases set automatically add them to '.tcshrc' in your home directory. Here are a couple
of examples:
alias xps "ps auxc | grep -i \!:1"
'xps' will now print the process id (PID) for a given command.
xps OmniWeb
melkor 965 0.0 7.0 157072 59540 ?? S 79:23.50 OmniWeb
For an easy grep:
alias xgrep "egrep -ilrs \!:1 *"
Unix_tricks.txt Page 19 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
'xgrep' will search all files in the directory hierarchy for the given string, case insensitive,
printing a list of files that match the string.
xgrep osxfaq
~ melkor % xgrep osxfaq
Development/.FBCIndex
Library/Addresses/Address Book.addressbook
Library/Application Support/AOL Instant Messenger (SM)/Buddies Cache
Library/Application
Support/Chimera/Profiles/default/eb0lqzsx.slt/cookies.txt
Library/Application Support/Chimera/Profiles/default/eb0lqzsx.slt/cookperm.txt
...
Periodic Aliases
Run any command periodically.
The alias 'period' is run every 'tperiod' minutes. For example:
alias periodic date
set tperiod=30
will execute the 'date' command every 30 minutes.
Shell Special Aliases
Special aliases are executed by the shell at specific times.
'precmd' runs just before each prompt is printed.
'postcmd' runs before each command gets executed.
'shell' specifies the interpreter for executable scripts which do not themselves specify an
interpreter.
For example:
alias precmd 'printf "\033]0; `pwd` $host@$user xxx\007"'
will update the Terminal window title bar with the current directory and the host and user name
each time you issue a command (in particular 'cd').
Recently Modified Files
Find is able to search files dependant on their time-stamp.
To find all files in your home directory modified within the last 24 hours, for example to do a
daily backup, use:
find ~ -mtime 1
Find This or That
Find has a very powerful syntax and can combine criteria.
To find all HTML and WebSiphon (.ws) files in ~/Sites use:
find ~/Sites \( -name "*.ws" -or -name "*.html" \)
Unix_tricks.txt Page 20 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Find and Exec
Use the power of find to root out the files you are interested in, and then apply a given command
to each file found.
For example, to search all HTML files in your Sites directory, use the -exec option to invoke
grep.
find . \( -name "*.htm" -or -name "*.html" \) -exec grep -il warning {} \;
Search
Make an alias of the find command to do common searches.
For example to search for strings in text files:
alias ftxt 'find . -name "*.txt" -exec grep -il \!:1 {} \;'
Then type:
ftxt hello
to find all .txt files containing hello.
Ensure that the aliases you create do not have the same name as existing commands.
Restore the 10.1 Shell Defaults
For whatever reason, the default shell initialization that came with Mac OS X 10.0 and 10.1 is
included but not activated in 10.2.
If you notice that your Terminal (or more correctly the shell) does not behave as it used to, and
you would like the behaviour restored, type:
cat /usr/share/tcsh/examples/README
and follow the instructions.
It is not necessary to follow this part:
mkdir ~/Library/init/tcsh
as you can add your own customisations to the files you have just created:
~/.tcshrc
~/.login
~/.logout
(Note that ~ or tilde is shorthand for your home directory.)
Some Useful Settings
If you do not wish to follow Monday's tip, you can restore a few of the more useful shell
features missing in 10.2 by adding these commands to the tcsh initialization file '~/.tcshrc'.
set autolist # List autocomplete alternatives when tab pressed
set correct = cmd # Correct misspelled commands
set savehist = 100 # Save command line history when Terminal closed
As an example of the first two settings:
cd ~
ls D <hit the tab key>
Desktop/ Development/ Documents/
Unix_tricks.txt Page 21 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
mun ls
CORRECT>man ls (y|n|e|a)?
...
Type:
set
to see all the shell's variables.
Change History
Be default, the size of your command line history is 100 or 150 lines. You can increase this with
the commands:
set history = 2000 # History remembered is 2000 commands.
set savehist = (2000 merge) # Save and merge with existing saved commands.
These should be added to your tcsh initialization file '~/.tcshrc'.
This saves the last 2000 commands. Notice the merge option, this is very useful if you have more
than one Terminal window open at a time. Instead of the last Terminal session overwriting the
history saved by the others, history is merged in with previously saved history.
So Insensitive
By default, command and filename completion by the tab key is case sensitive. To make it non case
sensitive add the following to your tcsh initialization file '~/.tcshrc':
set complete = enhance # Complete case insensitive.
Enhance also considers '-' and '_' to be equivalent.
By way of an example:
cd ~
ls d <hit the tab key>
Desktop/ Development/ Documents/
The Path Home
You may not realise this, but typing, for example:
cd /
cd Desktop
will find the directory Desktop even though it is not in the current directory. 'cd'
automatically searches your home directory if it cannot find the specified directory. It does
this because (or if) the 'cdpath' variable is set. If this does not work for you try:
set cdpath = (~)
You can add more directories to this search path with:
set cdpath = (~ ~/Sites)
If 'cd' cannot find the given directory in the current directory, it will search your home
directory (~), then 'Sites' in your home directory.
cd /
cd data-base
Unix_tricks.txt Page 22 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
goes to
~/Sites/data-base
Note that autocompletion does not follow the 'cd' search path.
Auto Complete in Color
The 'tcsh' shell has a built-in 'ls' that it uses for auto-completion. This can be made to list
in colour by setting the shell variable 'color'
set color
Now type:
ls <now hit control-d or tab if you have autolist enabled>
(You must type a space after the 'ls')
The auto completion will now be shown in colour.
If tab completion does not work for you, set the autolist variable:
set autolist
List in Color
Following on from the previous tip, one can use the 'tsch' built-in colour 'ls' instead of the
regular 'ls'. Remember to set the shell variable 'color'.
Type (with no spaces):
ls-F
You may want to make an alias to avoid typing the '-F' part:
alias ls ls-F
and put this line in your ~/.tcshrc file.
Changing Color
If you wish to change the colors that 'ls' and auto-completion use, set the shell environment
variable LS_COLORS.
As an example, add this, as one long line with no spaces, to your ~/.tcshrc file.
setenv LS_COLORS 'no=00:fi=00:di=00;33:ln=04:ex=32:
*.sh=32:*.tar=31:*.tgz=31:*.gz=31:*.sit=31:
*.jpg=36:*.jpeg=36:*.gif=36:*.psd=36:*.html=35:*.htm=35:*.css=35:'
The first line defines file type versus colours; the second and third file extensions versus
colours. You can customise this as you wish.
File types are:
no Normal (non-filename) text
fi Regular file
di Directory
ln Symbolic link
pi Named pipe (FIFO)
so Socket
bd Block device
cd Character device
Unix_tricks.txt Page 23 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
ex Executable file
mi Missing file
or Orphaned symbolic link
Colours are:
0 default Terminal colours
1 brighter colour
4 underline
5 flashing
30 black foreground
31 red foreground
32 green foreground
33 dark yellow foreground
34 blue foreground
35 magenta foreground
36 cyan foreground
37 dark white (grey) foreground
40 black background
41 red background
42 green background
43 dark yellow background
44 blue background
45 magenta background
46 cyan background
47 dark white (grey) background
Shut Up!
If you use auto-completion frequently then the shell's constant beeping may annoy you, especially
if you have the sound turned up while listening to iTunes. Set the shell variable matchbeep to
stop this.
set matchbeep=never
Add this to your ~/.tcshrc file to make the change permanent.
An alternative is to set it to beep only when no match can be found:
set matchbeep=nomatch
Really Shut Up!
To tell the shell never to beep issue the command:
set nobeep
(This is not the same as the Terminal Window Settings --> Emulation - no beep option, which stops
all programs run within the Terminal from beeping.)
ditto
You may be aware that Unix commands do not understand Mac resource forks, and so commands like
'cp' cannot be used to copy files that have them.
The command 'ditto' does. It can do a recursive copy of an entire directory structure. It does
this as a 'carbon copy' of the directory, preserving owners, permissions, timestamps, etc. It's
an ideal way to backup the /Users directory.
The command:
Unix_tricks.txt Page 24 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
appletalk - configure AT
Kill All!
'killall' is new in OS X 10.2, but not specific to OS X.
This will kill a process by name. The good old 'kill' command requires the PID (Process ID).
For example:
killall Clock
does the same job as:
ps -cx
PID TT STAT TIME COMMAND
170 ?? Ss 2:50.85 ATSServer
182 ?? Ss 19:37.36 Window Manager
...
1493 ?? S 0:00.49 Clock
813 std S 0:01.05 tcsh
kill 1493
Remember that Unix is case-sensitive, so 'killall clock' will not work.
User ID and Group ID
To display a user's name and primary group, followed by all the groups to which that user
belongs, use 'id'. This displays both the numeric ID and textual name.
Use:
id
uid=501(saruman) gid=20(staff) groups=20(staff), 0(wheel), 80(admin)
for the currently logged in user.
Use:
id root
uid=0(root) gid=0(wheel) groups=0(wheel) 1(daemon) 2(kmem) 3(sys) 4(tty) 5(operator) 20(staff)
31(guest) 80(admin)
for a given user, eg root.
Note that admin users pre 10.2 will belong to groups as above. Admin users created in 10.2 do not
belong to group wheel
Use:
groups
staff wheel admin
to list just the names of groups to which a user belongs.
Or:
groups root
wheel daemon kmem sys tty operator staff guest admin
for a given user.
Unix_tricks.txt Page 26 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
List Current Users
To list the names of all users currently logged in, user 'users'. Just to check no one is
sneaking onto your Mac.
users
saruman evil-cracker
You will only see genuine logins, not ftp users, or users started with 'su'.
Hopefully you will be the only user listed.
Who is Logged in?
Use 'who' to determine who is logged into your Mac. This differs from 'users' in that it lists
each Terminal connection, not each user. A user connected to three Terminal sessions will be
listed three times by 'who', but only once by 'users'.
who
saruman console Oct 13 16:36
saruman ttyp1 Oct 27 21:38
saruman ttyp2 Oct 27 23:33
saruman is connected 3 times, once as the original login when the machine boots up (console), and
twice in the Terminal (ttyp#).
You can also use the command 'w', which saves a whole two characters of typing and gives a little
extra information.
w
12:01AM up 14 days, 8:26, 2 users, load averages: 1.49, 1.19, 1.03
USER TTY FROM LOGIN@ IDLE WHAT
saruman co - 13Oct02 14days -
saruman p2 - Sun11PM 0 -
It also gives the information provided by 'uptime', telling how long your machine has been up and
running between re-boots.
Who Am I?
Severe case of amnesia?
whoami
saruman
Useful if you 'su' to other users.
whoami
saruman
su janice
Password:
whoami
janice
To get a history of logins since quite a long time ago (the last time your Mac tidied its log
files) use:
last
Unix_tricks.txt Page 27 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
saruman ttyp1 Sun Oct 27 15:26 - 15:26 still logged in
saruman ttyp1 Sat Oct 26 19:54 - 15:26 (20:32)
saruman ttyp1 Sat Oct 26 19:54 - 19:54 (00:00)
saruman ttyp1 Thu Oct 24 17:17 - 19:54 (2+02:36)
saruman ttyp1 Thu Oct 24 17:17 - 17:17 (00:00)
saruman ttyp1 Tue Oct 22 21:39 - 17:17 (1+19:37)
saruman ttyp1 Tue Oct 22 21:39 - 21:39 (00:00)
saruman ttyp1 Sun Oct 20 15:51 - 21:39 (2+05:48)
saruman ttyp1 Sun Oct 20 15:51 - 15:51 (00:00)
saruman ttyp1 Sat Oct 19 16:14 - 15:51 (23:37)
saruman ttyp1 Sat Oct 19 16:14 - 16:14 (00:00)
saruman ttyp1 Sat Oct 19 13:34 - 16:14 (02:39)
saruman ttyp1 Sat Oct 19 13:34 - 13:34 (00:00)
saruman ttyp2 Wed Oct 16 23:02 - 15:38 (10+17:35)
.....
Screen Capture
The Mac OS X Screen Capture program 'Grab' can be called from the command line with:
screencapture ~/Pictures/sc1.pdf
to capture the whole screen to the given file, in pdf format. The option '-c' is available to
force the screen capture to go to the clipboard instead of a file.
Alternatively:
screencapture -i ~/Pictures/sc2.pdf
will start in interactive mode where:
control key - causes screen shot to go to clipboard
space key - toggle between mouse and window selection modes
escape key - cancels interactive screen shot
Apple System Profiler
The Apple System Profiler in /Applications/Utilities can be called from the command line to write
a profile report to the Terminal.
For example:
AppleSystemProfiler > ~/profile.txt
will write a profile report to the given file.
Unix_tricks.txt Page 28 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Note: this worked well in 10.1 but seems to be flaky in 10.2.
Software Update
The Software Update utility can be called from the command line. It will list the updates
available and allow one to select an update for installation.
For example:
softwareupdate
Software Update Tool
Copyright 2002 Apple Computer, Inc.
Software Update found the following new or updated software:
- 3280
AirPort Software (2.1.1), 4350K - restart required
- 3283
Internet Explorer 5.2 Security Update (5.2.2), 9330K
- 3359
QuickTime (6.0.2), 19620K - restart required
To install an update, run this tool with the item name as an
argument.
e.g. 'softwareupdate <item> ...'
Energy Saver
The settings controlled by the Energy Saver system preference pane can also be set from the
command line.
Use pmset (power management set) to control:
- the screen dim time (dim)
- the disc spin-down time (spin)
- the sleep time (sleep)
including the individual battery (-b) and charger (-c) settings of an iBook or PowerBook, or all
(-a).
For example:
pmset -a dim 5 spin 5 sleep 20 womp 1
will set the screen dim, disc spin-down, and sleep time for all (battery and charger) settings.
The 'womp 1' parameter says to 'Wake on Magic Packet' and is the equivalent of the 'Wake for
network administrator access' check box in the Energy Saver preference pane.
Preferences
Preference can be set from the command line. Applications present preference panes in which the
user can set preferences. These are recorded in ~/Library/Preferences/pref-file-name.plist.
These preferences can be viewed and set from the command line using 'defaults'.
For example, to view the Terminal settings:
Unix_tricks.txt Page 29 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Running Processes
The 'ps' command lists running processes; by default just those of the current user that are
running within a Terminal window.
ps xc
will display a list of all your processes; and:
ps axc
will display a list of all processes for all users.
The options 'l' and 'u' display more information for each process, and the option 'ww' avoids
truncation of long lines.
A Useful Alias for 'ps'
Create an alias to 'ps' a given application by name.
Type this:
echo "alias xps 'ps aucx | grep -i \\!:1'" >> ~/.tcshrc
to add the alias to your shell startup script, then start a new shell or Terminal window.
To get information for 'Clock' type:
xps clock
saruman 3677 0.0 0.5 72676 3552 ?? S
7:05PM 0:03.30 Clock
Kill(all)
To (force)quit an errant process use the kill command. This takes the process id (or PID) as a
parameter
For example, to kill the clock application:
open /Applications/Clock.app/
ps cx
PID TT STAT TIME COMMAND
178 ?? Ss 7:19.41 ATSServer
...
3587 ?? S 0:00.58 Clock
3359 p4 S 0:00.60 tcsh
3377 std S 0:00.52 tcsh
kill 3587
If the Clock refuses to lay down and die:
kill -KILL 3587
'-KILL' says to force quit the application instead requesting it to quit.
OS X 10.2 includes the 'killall' command which can kill processes by name.
killall Clock
(This is case sensitive.)
Unix_tricks.txt Page 31 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
What is Whatis?
The command 'whatis' displays a brief one-line summary of the given command.
% whatis ls
ls(1) - list directory contents
'whatis' actually searches a database containing a one-line summary of all commands taken from
the man pages. It displays any lines that contain the search term as a complete word.
To update this database after you have added new commands do this:
sudo /usr/libexec/makewhatis man-path
where man-path is the directory containing the man pages. Most likely this will be
/usr/local/share/man for commands you have added yourself.
Is Apropos Appropriate?
The command 'apropos' gives a brief one-line summary for a given command.
% apropos cd
cd9660.util(8) - helps mount, probe, and unmount cd9660 filesystems
gluQuadricDrawStyle(3) - specify the draw style desired for quadrics
mount_cd9660(8) - mount an ISO-9660 filesystem
mount_cddafs(8) - mount an Audio CD
...
This is the same as Monday's 'whatis' except that the search term does not have to be a complete
word, as illustrated for 'cd' above. It searches the same database.
Note that 'man -k' is equivalent to 'apropos'.
Which Which?
'which' informs you which alternative of a given command would be executed if it were typed at
the command line.
For example, I have two versions of 'ls', one in /usr/bin and one in /usr/local/bin. I also have
an alias called 'ls'.
% which ls
ls: aliased to ls --color=tty
tells me that the alias will be run, and also what the alias will be expanded to.
Now try this:
~ % which which
which: shell built-in command.
This tells me which 'which' I executed - and it was in fact the shell's built-in 'which' - hence
its knowledge of aliases.
Escaping From Aliases
Unix_tricks.txt Page 32 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
This is a handy trick if you wish to run a command - and you want to run the actual executable -
but it has been aliased or the shell has a built-in version.
For example, Wednesday's tip told us that 'ls' was aliased. In order to run the original 'ls' I
could give the full pathname (if I knew it):
% /bin/ls
Alternatively I can escape the command with '\':
% \ls
Which Which is Which?
Following on from Wednesday's and Thursday's tips, I now wish to know which 'ls' I am running
when I type:
% \ls
(ie the non-aliased 'ls').
I can't simply type:
% which ls
ls: aliased to ls --color=tt
because this just tells me about the alias 'ls'. Remember that from:
~ % which which
which: shell built-in command.
'which' is also built-in, which is why it considers aliases.
To tell us about non-built-in commands we can do one of two things.
From this weeks tips it should be possible to guess at the commands.
Or scroll down for the answer.....
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
either 1)
~ % which \ls
/usr/local/bin/ls
Unix_tricks.txt Page 33 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
or 2)
% \which ls
/usr/local/bin/ls
And to determine which 'which' is which:
% \which which
/usr/bin/which
or:
% which \which
/usr/bin/which
I hope you followed that! :-)
The Path to Execution
When you execute a command the shell first looks for that command by following a path from one
'bin' directory to another (after first resolving aliases and checking if the command is built in
to the shell itself).
The path is described by the environment variable PATH. You can check the path with:
echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
A Longer Path
If you wish to put commands in a new bin directory such as:
/Users/you/bin
add this directory to your path so commands will be found automatically, without typing the full
pathname.
% echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
% setenv PATH /Users/saruman/bin:$PATH
% echo $PATH
/Users/saruman/bin:usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
(You could instead use ~/bin.)
Add the 'setenv' command to ~/.login to have the path set automatically when you login.
paths and PATHs
Two Paths?
% echo $path
/Users/saruman/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin
% echo $PATH
/Users/saruman/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
What is the difference between 'path' and 'PATH'?
'path' is a shell variable and 'PATH' is an environment variable. You may set either, and
whichever is changed the shell will synchronise the other.
Unix_tricks.txt Page 34 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Dotty Paths
If you write a shell script and wish to execute it, simply typing:
% my_script
my_script: Command not found.
will not work even though my_script is in the current directory. Why? Is the shell blind? Yes, it
is blind to all exectuables except those on the PATH.
You must type
% ./my_script
script runs....
By giving a pathname the shell does not need to use the search path.
To avoid doing this add the current directory '.' to the search path:
% echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
% setenv PATH "${PATH}:."
% echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:.
Warning: adding '.' to your path has security implications.
Rehash the Cache
Ok, previously I said that the shell searches PATH to find commands. Well, it doesn't really.
When the shell first starts, or when the PATH is changed, it follows PATH and remembers (caches)
all the commands in each directory.
When you execute a command the shell simply looks in its cache, which is much quicker than
searching PATH. Consequently when you add a new command or script it will not be found.
rehash
will cause the shell to recreate its cache.
Note that the 'bash' shell rehashes automatically.
If you think about it: adding '.' to the path as in Thursday's tip, the shell must treat it as a
special case.
About disktool
'disktool' will unmount, mount, rename, or eject drives and partitions.
disktool
will list the options. It does not have a manual page.
You must supply device names to 'disktool', not disk or volume names.
/dev/disk0 is a disk
/dev/disk0s9 is a partition (or slice) of disk0.
Type:
disktool -l
Unix_tricks.txt Page 35 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
to determine which volume names (also called mount points) correspond to which /dev/... device
names.
Refresh Disk Icons with disktool
If you mount a server or drive using the command line 'mount_afp' or 'mount_hfs', the Finder may
not show the icon in 'Computers'. Use:
disktool -r
to refresh the Finder's view.
Eject with disktool
Use 'disktools' to eject a CD that seems stuck.
disktool -l
will tell you which disk represents the CD.
disktool -l
*** Unrecognized disk appeared on disk1 ***
*** Unrecognized disk appeared on disk1s1s1 ***
***Disk Appeared ('disk1s1s2',Mountpoint =
'/Volumes/Return to Zork', fsType = 'hfs', volName =
'Return to Zork')
...
To eject 'Return to Zork' (disk1) use:
disktool -e disk1
disk1 device will attempt to be ejected ...
***Notifications Complete for type 1
***Disk Unmounted('disk1')
***Disk Unmounted('disk1s1s1')
***Disk Unmounted('disk1s1')
***Responding yes to unmount - disk1s1s2
***Disk Unmounted('disk1s1s2')
***Responding yes to eject - disk1
***Responding yes to eject - disk1s1s1
***Responding yes to eject - disk1s1s2
***Responding yes to eject - disk1s1
***Disk Ejected('disk1')
Unmount with disktool
You can use 'disktool' to unmount whole disks or individual partitons. For example, I wish to
unmount the partition /Volumes/Backup.
First use 'mount' or 'disktool -l' to determine the device:
mount
/dev/disk0s9 on / (local)
devfs on /dev (local)
fdesc on /dev (union)
<volfs> on /.vol (read-only)
/dev/disk0s10 on /Volumes/Grima (local)
Unix_tricks.txt Page 36 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
If you've ever tried to do a long list (ls -l) on the directory /etc:
ls -l /etc
lrwxrwxr-t 1 root admin 11 Nov 22 14:03 /etc -> private/etc
you will have found that it does not list the directory contents. This is because /etc is a link
(to /private/etc/).
To list the contents use either:
ls -l /private/etc/
or better, use the '-L' option:
ls -lL /etc
total 1256
-rw-r--r-- 1 root wheel 753 Jul 15 04:03 6to4.conf
-rw-r--r-- 1 root wheel 515 Jul 14 07:20 afpovertcp.cfg
-rw-r--r-- 1 root wheel 6 Nov 22 15:22 appletalk.cfg
-rw-r--r-- 1 root wheel 256 Nov 22 15:23 appletalk.nvram.en1
.......
The Quickest way to List a Directory
Q. How can you list the current directory with one keystroke (ls takes 3 including the return)?
A. Simply hit the tab key on an empty command line.
(This tip assumes you have 'autolist' set. If not enable it by adding the line
set autolist
to your ~/.tcshrc file.)
Distinguish File Types
If you don't use colour 'ls', then a simple:
ls
will not give any indication of what a file is - plain file, directory, link, executable, etc.
Use option '-F'
ls -F
directory/ file1 file2 link1@ my_script*
* @ = link
* / = directory
* * = executable
The next tip will be on Monday 30th. Until then have a good:
grep 12/25 /usr/share/calendar/*
and happy birthday Sir Isaac Newton!
Kill A Command Line
I hope Santa delivered that new Mac you asked for :-).
If you have started to type a command line and wish to abandon it, rather than back-deleting a
character at a time, type control-u to delete the whole line.
Unix_tricks.txt Page 39 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
(Also, use control-k will delete from the cursor to the end of the line.)
Deja Vu
We all know that pressing the up arrow key moves back through the command history. But this is
tedious if the desired command is a long way back in the history sequence.
To jump straight back to a command, type the first part of it then hit esc-p (the escape or 'esc'
key followed by 'p'). Another esp-p will jump back further to a previous match.
Have a happy:
grep 01/01 /usr/share/calendar/*
and happy birthday the Internet, 20 years old!
Glob Now
If I type:
rm fil?*
how can I check exactly which files will be removed before I hit return?
Either precede the command with:
ls fil?*
or expand the globbing ('*' '?' or '[]' wildcard characters).
Typing control-x followed by '*' will expand 'fil?*' onto the command line:
rm fil?* (hit control-x followed by *)
You will end up with an expanded command line like:
rm file1 file2
Hit return if this is ok, otherwise hit control-u to delete the command line (instead of the
files!).
List Globbing
I hope the new year's hangover has subsided and you are no longer seeing double!
Similar to Wednesday's tip, to list an expanded globbing rather than expanding it to the command
line, hit control-x followed by G (upper case G).
ls fil?* (hit control-x followed by G)
file1 file2
ls fil?*
Similar to Wednesday's tip, to list an expanded globbing rather than expanding it to the command
line, hit control-x followed by G (upper case G).
ls fil?* (hit control-x followed by G)
file1 file2
ls fil?*
Which Command?
As in a previous tip, the command:
Unix_tricks.txt Page 40 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
which ls
ls: aliased to ls --color=tty
says which variant of 'ls' will be executed.
Following a command with escape then '?' will also do this:
ls (hit escape followed by ?)
/usr/local/bin/ls --color=tty
ls
What's the difference?
Use the "diff" command to compare two files. "diff" will report on all lines that differ, have
been added, or have been deleted. Diff assumes the first file is the original and the second file
is the newer.
cat f1.txt
This line in both
This line has File One
This line in both
This Line not in File Two
Completely different in each file
cat f2.txt
This line not in file one
This line in both
This line has File Two
This line in both
Not at all the same as in file one
diff f1.txt f2.txt
0a1
>This line not in file one
2c3
< This line has File One
---
> This line has File Two
4,5c5
< This Line not in File Two
< Completely different in each file
---
> Not at all the same as in file one
The "< and "> characters indicate which file. Imagine each as an arrow pointing to the position
of the file name on the "diff" command line.
"0a1" means line 1 of f2.txt is an addition compared to f1.txt.
The "2c3" indicates line 2 of f1.txt has changed in f2.txt, and is line 3 because of the
additional line as above.
What's a Little White Space Between Friends
Sometimes files may differ in insignificant ways. To ignore only differences in white space
(spaces and tabs) use option '-b'. To ignore differences due to blank lines use option '-B'.
diff -bB f1.txt f2.txt
The '-q' option can also be useful.
diff -bBq f1.txt f2.txt
Unix_tricks.txt Page 41 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
NOTE: Use a >> not a single >. The double redirect appends to a file, the single redirect will
start a new file overwriting any existing file of that name.
Edit With 'cat'
Use the 'cat' command to create a new file, or add to the end of an existing file.
cat > newfile.txt
Type your text
here until you have
finished then
type control-d
To add to the end of an existing file use double redirect:
cat >> newfile.txt
type here...
Edit with 'printf'
Here is a neat trick using printf.
ls
file1 file2 file3 file4 file5
( printf "<pre>\n" ; printf "<tt>%s</tt>\n" `ls` ; printf "</pre>\n" ) > ~/list.html
cat ~/list.html
<pre>
<tt>file1</tt>
<tt>file2</tt>
<tt>file3</tt>
<tt>file4</tt>
<tt>file5</tt>
</pre>
'sort'
Command 'sort' will sort the lines of a text file alphabetically (or alphanumerically).
For example:
cat to-sort.txt
ls 3 list files
cd 2 change directory
pwd 4 print working directory
grep 1 search files for regular expression
sort to-sort.txt
cd 2 change directory
Unix_tricks.txt Page 44 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
grep 1 search files for regular expression
ls 3 list files
pwd 4 print working directory
The sorted output is written to the Terminal. Use redirection or option '-o' to write to a file.
DON'T write the output back to the input file or you will end up with an empty file.
Mac OS X uses GNU 'sort'.
Sort on Another Field
Command 'sort' will sort on any field, where fields are separated by white space.
For example, to make the second field the sort key use:
cat to-sort.txt
ls 3 list files
cd 2 change directory
pwd 4 print working directory
grep 1 search files for regular expression
sort -k2 to-sort.txt
grep 1 search files for regular expression
cd 2 change directory
ls 3 list files
pwd 4 print working directory
In this example the files were separated by tab characters.
Changing the Field Separator
Tuesday's example sorted a file using the second field of each line, and 'sort' assumed that
fields were separated by white space.
Q. What if fields are separated by a character other than white space?
A. We can specify a custom separator with option '-t', as in:
cat to-sort.html
<a href="../ls">list files
<a href="../cd">change directory
<a href="../pwd">print working directory
<a href="../grep">search files for regular expression
Unix_tricks.txt Page 45 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
sort -t\> -k2 to-sort.html
<a href="../cd">change directory
<a href="../ls">list files
<a href="../pwd">print working directory
<a href="../grep">search files for regular expression
Now the file is sorted according to the description rather than the URL.
(Note that '<' is escaped to prevent the shell from interpreting it as a redirection character -
see Tutorial 7.)
Space Separated Fields
You may have a file where the fields are separated by multiple spaces, rather than tabs as in
Tuesday's example. Using 'sort' results in rather odd behaviour. This example attempts to sort by
field 2, the sequence number.
cat to-sort.txt
ls 3 list files
cd 2 change directory
pwd 4 print working directory
grep 1 search files for regular expression
sort -k2 to-sort.txt
cd 2 change directory
ls 3 list files
pwd 4 print working directory
grep 1 search files for regular expression
Use option '-b' to ignore leading blanks when finding sort keys:
sort -b -k2 to-sort.txt
grep 1 search files for regular expression
cd 2 change directory
ls 3 list files
pwd 4 print working directory
Sorting and Merging Files
Command 'sort' can be used to sort and merge two or more files.
Unix_tricks.txt Page 46 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
cat s1.txt
9
7
1
4
cat s2.txt
2
3
8
6
5
sort s1.txt s2.txt
1
2
3
4
5
6
7
8
9
Recall a Command by Name
If you wish to repeat a previously issued command, then type '!' followed by enough of the
command to make the recall unambiguous.
For example:
!pri
will recall, for me currently, the last issued 'printenv' call:
!printenv
Recall a Command by Number
Issuing the command 'history' will list a numbered history of commands. You can recall any
command with '!n' where 'n' is the command number given by the history list.
history
1 09:31 cd ~
...
95 23:04 printenv
96 23:07 echo "Hello"
97 23:07 ls -al
98 23:07 vim index.ws
99 23:13 history
100 23:13 ls
Unix_tricks.txt Page 47 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
!96
echo "Hello"
Hello
The last command is recalled with:
!!
and the ones before that with:
!-2
!-3
etc.
If your history is long, type
history n
to display just the last n commands.
Recall and Tabbed Completion
Following on from Monday's tip, if you want to be sure of the command to be executed by:
!pri
hit the tab key and tcsh will complete the command and wait for you to hit return to execute it,
or control-u to dismiss it.
Search and Recall
A command may be recalled by giving a search string. The last issued command that matches the
string will be executed.
For example:
history
...
2044 23:26 ls -al
2045 23:26 vim log.txt
2046 23:26 ls
2047 23:26 cat log.txt
2048 23:27 cat index.ws
2049 23:27 history
!?.txt?
cat log.txt <-- this command will be found and issued
Unix_tricks.txt Page 48 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Recalling Command Arguments
The individual arguments of a previously issued command may be recalled.
For example:
less a-very-long-filename-to-type-again
vi !!:1
vi a-very-long-filename-to-type-again <-- this command will be issued
!!:0 is the command itself, and !!:1 is the first argument, !!:2 the second, and so on.
It is also possible to specify arguments of other commands with !n:n and !-n:n (see Tuesday's
tip).
The 'cd' Dash
Suppose you change directory to issue a few commands, and then wish to jump back to the original
directory. To save retyping the original directory name one can make use of the shell's special
'old working directory' shortcut '-'.
Start here:
pwd
/Users/saruman/Documents/My Setup
Change directory temporarily:
cd /System/Library/Displays/Overrides/Contents/Resources/English.lproj/
pwd
/System/Library/Displays/Overrides/Contents/Resources/English.lproj
...
Now jump back to the original directory:
cd -
pwd
/Users/saruman/Documents/My Setup
The Old Working Directory
The shell keeps a record of the current and old working directories in variables 'cdw' and 'owd'.
cd ~
cd Documents/My\ Setup/
echo $cwd
/Users/saruman/Documents/My Setup
echo $owd
/Users/saruman
Monday's trick is equivalent to:
cd $owd
Back and Forth
Following on from Monday's tip, when you issue 'cd -' the current and old working directories are
effectively swapped. This makes it possible to jump back and forth between two directories.
cd ~
Unix_tricks.txt Page 49 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
cd Documents/My\ Setup/
pwd
/Users/saruman/Documents/My Setup
cd -
pwd
/Users/saruman
cd -
pwd
/Users/saruman/Documents/My Setup
cd -
pwd
/Users/saruman
Implicit CD
Setting the shell variable 'implicitcd' causes the shell to treat a directory name typed without
'cd' as a request to change to that directory.
cd ~
ls
Desktop Documents Movies Pictures Sites bin osxfaq
Development Library Music Public Temporary Items
Sites
Sites: Permission denied.
set implicitcd
Sites
pwd
/Users/saruman/Sites
Naturally, if a command of the same name exists, the command is executed in preference to
changing directory.
cdpath
How does the 'cd Sites' command below work?
cd /System
ls
Library
cd Sites
ls
Tips Tutorials archive areas css frames images index.html
pwd
/Users/saruman/Sites
The command 'cd Sites' should not work, but it has managed to change directory to 'Sites' in my
home directory. This behaviour is controlled by the shell variable 'cdpath'
set cdpath= ( ~ ; ~/Sites )
Tells 'cd' that if it can't find the requested directory in the current directory, search my home
directory (~), and then ~/Sites
So Insensitive
(See also Weeks 1 and 13)
As Mac OS X is case insensitive, it makes sense to search for files in a case insensitive way.
Instead of using 'find -name' use 'find -iname'.
Unix_tricks.txt Page 50 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Multiple Filters
(See also Weeks 1 and 13)
Following on from Wednesday's tip, suppose I wish to find the weekly tips for each Monday. I can
apply a second filter to 'find' as in:
find ~ -path "*week*" -name "monday.ws"
/Users/saruman/osxfaq/templates/tips-week-template/monday.ws
/Users/saruman/Sites/Tips/unix-tricks/week1/monday.ws
/Users/saruman/Sites/Tips/unix-tricks/week10/monday.ws
/Users/saruman/Sites/Tips/unix-tricks/week11/monday.ws
...
All of the files that pass the '-path' filter are filtered again by the '-name" filter. Only
files that pass both filters will be displayed.
Locate All Empty Files and Directories
(See also Weeks 1 and 13)
find . -empty
will do just that for the current directory.
To remove them use:
find . -empty -delete
The delete option will remove files and directories. If a directory contains just empty files,
the files will be deleted and then the directory, which has suddenly become empty, will also be
deleted.
Note that:
find . -size 0
will not detect empty directories as they have a non-zero size.
Pushing and Popping
If you issue 'cd' many times and then wish to retrace your steps back to the original directory,
use the shell's directory stack.
To change directory use:
pushd
To return use:
popd
Each command echoes the current directory stack.
For example:
pwd
/Users/saruman
pushd /System/Library/Extensions/
/System/Library/Extensions ~
pushd /Library/StartupItems/Tablet/
Unix_tricks.txt Page 52 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
/Library/StartupItems/Tablet /System/Library/Extensions ~
pushd /etc
/etc /Library/StartupItems/Tablet /System/Library/Extensions ~
popd
/Library/StartupItems/Tablet /System/Library/Extensions ~
pwd
/Library/StartupItems/Tablet
popd
/System/Library/Extensions ~
pwd
/System/Library/Extensions
popd
~
pwd
/Users/saruman
Silencing pushd and popd
Following on from Monday's tip, if you don't want pushd and popd to echo the current directory
stack, set the shell variable 'pushdsilent'.
set pushdsilent
Viewing the Push/Pop Stack
If you wish to view the current directory stack, useful if 'pushd' and 'popd' are silenced, issue
the command:
dirs
/Library/PreferencePanes /System/Library /Developer/Applications ~
If the stack is deep, use:
dirs -v
0 /Library/PreferencePanes
1 /System/Library
2 /Developer/Applications
3 ~
Saving the Directory Stack
The directory stack may be saved between login sessions by setting the shell variable 'savedirs'
set savedirs
It may also be saved and loaded manually with the commands:
dirs -S
and
dirs -L
For example:
dirs
/Library/PreferencePanes /System/Library /Developer/Applications ~
dirs -S
dirs -c
dirs
Unix_tricks.txt Page 53 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
/Library/PreferencePanes
dirs -L
dirs
/Library/PreferencePanes /System/Library /Developer/Applications ~
Nore that 'dirs -c' was used to clear the directory stack.
Jumping to a Stacked Directory
'pushd' will jump to any directory in the stack by specifying '+n' where 'n' is the position in
the stack.
For example:
dirs -v
0 ~/Sites/Tips/unix-tricks
1 /Library/PreferencePanes
2 /System/Library
3 /Developer/Applications
4 ~
pwd
/Users/saruman/Sites/Tips/unix-tricks
pushd +3
pwd
/Developer/Applications
dirs -v
0 /Developer/Applications
1 ~/Sites/Tips/unix-tricks
2 /Library/PreferencePanes
3 /System/Library
4 ~
System Cron
The Unix cron daemon runs continually looking at /etc/crontab (the cron table). This file lists
commands against the time at which they should be run.
Add your own scheduled commands to this file (which must be edited as the super-user).
Unix_tricks.txt Page 54 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The '*' means 'on every'. Each of the fields are as commented in /etc/crontab.
User Crontabs
Create a user-specific crontab using the command 'crontab'
Create a file with each line of the form:
minute hour month-date month day-of-week command and argumants
Issue the command:
crontab
to enter the commands into your crontab.
To list your crontab:
crontab -l
and to remove it:
crontab -r
Note that the format of user crontabs is the same as that for the system crontab except that the
'who' field is omitted. Commands are run as the user who created the crontab file.
Stopping 'cron' From Spamming
'cron' will mail you the results of running you crontab commands. To prevent this (especially if
you do not have sendmail running) add:
MAILTO="
to the top of your crontab file. Remove the old crontab and enter the new one (see Tuesday's
tip).
crontab Tricks
The time fields in your crontab can be more complex than simple numbers. To run 'command' at
11am, 4pm, and 8pm, use:
0 11,16,20 * * * command
And to run every 10 minutes:
*/10 * * * * command
Time Shortcuts
Instead of the five number fields, crontab can take special strings:
@reboot run at startup.
@hourly once an hour == "0 * * * *"
@daily once a day == "0 0 * * *"
@weekly once a week == "0 0 * * 0"
Unix_tricks.txt Page 55 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The command:
bindkey -c ^xe "ls -alL /etc"
will bind the key sequence 'control-x e' to the ls command specified. Note that it is not
necessary to press return after the key sequence.
Filtering Repeated Lines
The command 'uniq' will report or filter out repeated lines in a file. It reads and writes the
standard streams, so file redirection is required to read and write files. The -c option reports
the repeat count.
'uniq' is useful for trimming log files where the same message is repeated many times. Multiple
blank lines are also filtered.
For example, to filter repeated lines in f3
cat f3
Line 1
Line 1
Line 2
Line 3
Line 4
Line 4
Line 4
Line 4
Line 4
Line 4
Line 5
Line 1
Line 1
Line 1
uniq -c
2 Line 1
1 Line 2
1 Line 3
6 Line 4
1 Line 5
3 Line 1
1
Comparing Files
The command 'comm' will report on lines that are common to two files. The files must be sorted
lexically which can be achieved with 'sort' (see Week 28).
For example:
cat f1 f2
A line
B line
D line
F line
A line
B line
C line
E line
Unix_tricks.txt Page 57 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
F line
comm f1 f2
A line
B line
C line
D line
E line
F line
The command 'diff' also compares files.
Extracting Common and Difference Lines
Using command 'comm' with options '-1', '-2', and '-3', one can extract common and difference
lines between two files.
Continuing from Tuesday's example, to extract the common lines between f1 and f2:
comm -12 f1 f2
A line
B line
F line
or:
comm -12 f1 f2 >f3
to write back to a new file. The options specify which columns to cut out of the report.
For the difference lines, use opton '-23' or '-13'
Splitting a Large File
Use the command 'split' to break a large file into multiple smaller ones.
split -b5k big-file
will split big-file into multiple 5k byte files.
And:
split -l200 big-file split_
will split big-file into multiple 200-line files, and the generated filenames will be prefixed
with 'split_'.
Merge Files with Paste
Paste will merge two files, line by line.
As an example:
cat f1 f2
Line 1
Line 2
Line 3
Line 4
rest of line 1
rest of line 2
rest of line 3
Unix_tricks.txt Page 58 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
rest of line 4
paste f1 f2
Line 1 rest of line 1
Line 2 rest of line 2
Line 3 rest of line 3
Line 4 rest of line 4
creating and extracting tar balls
Just as Aladdin Systems' StuffIt allows one to 'stuff' many files into a single '.sit' file, the
Unix 'tar' command allows one to create an archive (often called a tar ball). 'tar' was
originally used to archive to magnetic tape (tape archiver), but now a days it is more often used
to archive to a named file.
To archive the whole of the ~/Sites directory into a single file called sites.tar, use:
cd ~
tar cvf sites.tar Sites
The options 'cvf' do the following:
c - create a new archive
v - verbose, name each file as it is added to the archive
f - archive to a named file, in this case sites.tar
The original directory is untouched.
To decompress the archive use the command:
tar xvf sites.tar
This extracts the original files into a directory called 'Sites', which is created if it does not
already exist.
The option 'x' says to extract from existing archive.
zipping tar balls
'tar' is able to compress and decompress archives using Lempel-Ziv coding (LZ77) - aka zip.
Simply add the option 'z' when compressing and extracting.
tar czf sites.tgz Sites
and
tar xzf sites.tgz
Don't use capital Z as this compresses using 'compress', not zip.
adding to and listing archives
One can add files to an existing tar ball using option 'r' (replace?) instead of 'c'.
The command:
tar rf sites.tar bin
will add all the files in directory 'bin' to an existing archive called 'sites'tar'.
To examine an archive use option 't' (table of contents).
tar -tf sites.tar
Unix_tricks.txt Page 59 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
path names
The command:
tar cvf sites.tar Sites
differs from:
tar cvf sites.tar /Users/saruman/Sites
The first variant creates a tar file that extracts into 'Sites' in the current directory.
The second variant uses the whole path name, but strips the leading /, and creates a tar file
that extracts into Users/saruman/Sites in the current directory.
It's a good idea to always try:
tar -tf sites.tar
before actually extracting, so as you know where the extracted files are going to be placed.
absolute paths
The command:
tar cvfP sites.tar /Users/saruman/Sites
creates an archive that can be extract into /Users/saruman/Sites, independent of the current
directory. Compare this with Thursday's tip.
The option -'P' prevents the leading '/' from being stripped when the archive is made. However,
it will still be stripped when the archive is extracted unless the extract command also uses
option '-P'.
tar xvfP sites.tar
Zipping
I guess the zip compression utility needs no introduction. OS X comes with the GNU version, gzip.
To compress (zip) a file, use:
gzip filename
This will replace the file with a compressed version with the extension '.gz'
To unzip:
gunzip filename
or
gzip -d filename
The default level of compression (which is 6) can be changed. 1 (--fast) is the lowest level of
compression but the fastest algorithm, 9 (--best) is the highest level of compression but slowest
algorithm.
gzip --best filename
Zipping Several files
To compress more than one file into a single archive, use the '-c' option.
gzip -c filename1 filename2 > two-files.gz
Unix_tricks.txt Page 60 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Keep an eye on log files with (for example the apache access log):
% tail -f /var/log/httpd/access_log 217.207.57.220 - - [31/Mar/2003:14:22:15 +0100] "GET
/default.ida?XXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd
3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090
%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a
HTTP/1.0" 200 809 217.155.44.175 - - [31/Mar/2003:15:01:18 +0100] "GET /default.ida?XXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u7801
%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u9090
%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u00=a
HTTP/1.0" 200 809
This will update each time an entry is added to the end of the log. Control-c to stop. In this
case we see attacks by a Code Red variant, which is back with us at the moment. Of course,
running apache we are immune to this attack.
Learn About Samba
Issue the command:
% open /usr/share/swat/using_samba/index.html
Learn About Cups
Issue the command:
% open /usr/share/doc/cups/documentation.html
Separating Filename Components I
Use the following technique in tcsh scripts to extract components of a full pathname from a
variable or parameter.
The full pathname:
% set fn="/Users/saruman/Documents/Essentials/todo.rtf"
% echo $fn
/Users/saruman/Documents/Essentials/todo.rtf
Extract the pathname (head):
% echo $fn:h
/Users/saruman/Documents/Essentials
Extract the filename (tail):
% echo $fn:t
todo.rtf
Extract the extension:
% echo $fn:e
rtf
Unix_tricks.txt Page 62 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
% ls !*:r.html
ls *.html
a.html b.html c.html
I'll leave you to figure out what is happening.
basename and dirname
The commands basename and dirname extract the respective parts from a full pathname. These are
commands and can therefore be used in any script, unlike the tricks at the beginning of the week
that are specific to tcsh.
% echo $fn
/Users/saruman/Documents/Essentials/todo.rtf
% echo `basename $fn`
todo.rtf
% echo `dirname $fn`
/Users/saruman/Documents/Essentials
Extended 'find'
Sometimes one needs to apply the same command to many files, and using 'find' is a good approach.
You can write the following script to make this simpler.
% cat xfind
#!/bin/sh
if [ "$1" = "" ]; then
echo "Usage: `basename $0` filetype [command to -exec]"
exit
fi
if [ "$2" = "" ]; then
find . -name "$1"
else
find . -name "$1" -exec $2 {} \;
fi
This script is written in /bin/sh, rather than /bin/tcsh. Don't forget to make the script file
executable.
% xfind "*.html"
will list all matching files in the current directory and subdirectories.
% xfind "*.html" less
will display them.
Mac to Unix
Here is a script to change a file with traditional Mac end of line characters to have Unix end of
line characters.
% cat unix2mac
Unix_tricks.txt Page 64 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
#!/bin/sh
if [ "$1" = "" ]; then
echo "Usage: `basename $0` filename"
exit
fi
tr \\r \\n < $1 > $1.tmp ; mv $1.tmp $1
This script can be combined with the xfind script from Monday's tip:
% xfind "*.html" unix2mac
Unix to Mac
To complement Tuesday's script, here's the reverse:
% cat mac2unix
#!/bin/sh
if [ "$1" = "" ]; then
echo "Usage: `basename $0` filename"
exit
fi
tr \\n \\r < $1 > $1.tmp ; mv $1.tmp $1
Batch Rename
Here is a script to rename files in the current directory. It can be adapted to perform other
functions.
% cat rename
#!/bin/sh
#I'll leave the error checking to you....
for fn in *.$1
do
mv $fn `basename $fn $1`$2
done
The command:
% rename txt rtf
will change *.txt to *.rtf in the current directory.
Use Backquotes
Unix_tricks.txt Page 65 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Remember that enclosing a command in backquotes means we can pass its output as arguments to
another command.
For example, to edit all html files in a directory and its subdirectories (using Monday's 'xfind'
script):
% vi `xfind "*.html"`
(or vim, emacs, pico, bbedit...)
Enabling 'at'
The Unix command 'at' can be used to execute a given command at a given time. (But not on a given
date in OS X). However, a few tweaks must be made to OS X before it will work. These must be done
as root.
1. Uncomment (remove the # from) this line in /etc/crontab:
#*/5 * * * * root /usr/libexec/atrun
to check the queued 'at' jobs every 5 minutes.
2. Create the following directory if it does not already exist:
sudo mkdir /var/at/spool
3. Create an empty 'deny' file to allow all users to run 'at'
sudo touch /var/at/at.deny
(Or create 'at.allow' and add a list of the allowed users.)
(See week 33 for more scheduling commands.)
Using 'at'
Use the 'at' command to schedule a (sequence of) command(s) to be executed later. The following
example schedules the Clock application to be launched at 21:30 today. The commands to run can be
supplied either directly on the command line, or read from a file with the '-f' option.
% cat at-file
open /Applications/Clock.app
% at -f at-file 21:30
Job a010b400a.000 will be executed using /bin/sh
At 21:30 the Os X Clock application will be launched.
'atq' and 'atrm'
One can examine the list of jobs on the current user's 'at' queue with:
% atq
Date Owner Queue Job#
21:26:00 04/20/03 saruman a a010b400a.000
and delete jobs with:
% atrm a010b400a.000
passing the rather ugly job number.
Unix_tricks.txt Page 66 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
More on 'at'
When the list of commands given by 'at' are executed, they are done so by the /bin/sh shell. The
environment variables and the current directory are re-created to match those from when the 'at'
command was issued.
If commands need to refer to your home directory use:
$HOME
Times can be specified relative to now with:
at -f at-file now + 10 minutes
at -f at-file now + 1 hour
The Unix Calendar
Use the 'calendar' command to query appointments and events. Create a plain text file (default
name 'calendar') containing a month, date, and event on each line.
% cat calendar
10 April Thursday's job
20 April Sunday's job
26 April Saturday's job
04/01 April Fool
04/20 Sunday's job
04/29 Dentist
Print the events for a given date (specified as MMDD) with the following commands:
% calendar -d 0410
10 April Thursday's job
% calendar -d 0429
04/29 Dentist
For today (20 April in this example), you can miss out the date specification:
% calendar
20 April Sunday's job
04/20 Sunday's job
If the file is other than 'calendar' in the current directory use the '-f' option:
% calendar -f /path/name/file-name -d 0429
04/29 Dentist
Unix has some pre-written calendar files in:
/usr/share/calendar
For example:
% calendar -f /usr/share/calendar/calendar.holiday -d 0421
04/21 San Jacinto Day in Texas
04/22 Arbor Day in Nebraska & Delaware
04/22 Oklahoma Day in Oklahoma
04/21 Tiradentes in Brazil
ping
Unix_tricks.txt Page 67 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
;; Total query time: 47 msec
;; FROM: saruman.mayo-family.com to SERVER: default -- 212.23.8.1
;; WHEN: Sun May 4 20:47:50 2003
;; MSG SIZE sent: 27 rcvd: 252
% nslookup apple.com.
Server: ns0.zen.co.uk
Address: 212.23.8.1
Non-authoritative answer:
Name: apple.com
Address: 17.254.3.183
As ever, use the 'man' command to discover more.
'whois'
The 'whois' command queries whois.internic.net to discover the registrar, owner, and name servers
for a domain.
% whois mayo-family.com
Whois Server Version 1.3
...
Domain Name: MAYO-FAMILY.COM
Registrar: BULKREGISTER.COM, INC.
Whois Server: whois.bulkregister.com
Referral URL: http://www.bulkregister.com
Name Server: DNS2.INTERMAG.COM
Name Server: NS1.HOSTWIZARD.COM
Name Server: DNS3.INTERMAG.COM
Status: ACTIVE
Updated Date: 27-feb-2003
Creation Date: 19-jul-2001
Expiration Date: 19-jul-2003
...
If the domain was not registered by internic, a second whois is necessary. In this case a query
to the registrar's whois server is required using option '-h'.
% whois -h whois.bulkregister.com mayo-family.com
Adrian Mayo
54 Greenland Mills
Bradford-on-Avon, England BA15 1BL
UK
Unix_tricks.txt Page 72 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Domain Name: MAYO-FAMILY.COM
Administrative Contact:
Carlton Thomas domreg@gifford.co.uk
Gifford Internet Services
14 Tyrrel Way, Stoke Gifford
Bristol, England BS34 8UY
UK
Phone: +44 117 9397722
Fax: +44 117 9397733
Technical Contact:
Carlton Thomas domreg@gifford.co.uk
Gifford Internet Services
14 Tyrrel Way, Stoke Gifford
Bristol, England BS34 8UY
UK
Phone: +44 117 9397722
Fax: +44 117 9397733
Record updated on 2001-07-19 11:28:07
Record created on 2001-07-19
Record expires on 2003-07-19
Database last updated on 2003-05-04 15:58:47 EST
Domain servers in listed order:
DNS2.INTERMAG.COM 216.218.240.104
DNS3.INTERMAG.COM 198.144.200.118
NS1.HOSTWIZARD.COM 64.84.37.14
'whois' for non TLD
To do a 'whois' query on a non Top Level Domain, for example '.co.uk', one has to use another
'whois' service.
% whois -h whois.nic.uk bbc.co.uk
Domain Name:
bbc.co.uk
Registrant:
British Broadcasting Corporation
Registrant's Agent:
The British Broadcasting Corporation t/a BBC Technology [Tag = BBC]
Relevant Dates:
Last updated: 13-Mar-2001
Unix_tricks.txt Page 73 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Name servers listed in order:
ns.bbc.co.uk 132.185.132.21
ns1.thny.bbc.co.uk 38.160.150.21
ns1.thdo.bbc.co.uk 212.58.224.21
ns1.bbc.co.uk 132.185.132.11
WHOIS database last updated at 21:20:01 04-May-2003
And if anyone knows of a reliable way to determine the whois address for the various TLD and
country domains, let me know....
Sub Shells
To execute a command, or commands, in a new shell, bracket the commands.
To execute several commands on one line, separate them with a semicolon.
For example, compare the following two commands that make use of both of the above.
% (cd /; pwd); pwd
/
/Users/saruman
% cd / ; pwd ; pwd
/
/
Redirection
This applies to the 'tcsh' shell.
To redirect standard output use '>'. Eg:
ls -al * > list
Output and error messages write to different streams. To redirect both standard output and
standard error use '>&". Eg:
ls -al kfhsdhdjhsjkhf >& list
Redirection 2
The 'tcsh' shell does not provide an obvious way to redirect standard output and error to
different files. Use the following trick to do so:
(ls -al filename > list ) >& list.err
Redirection 3
The 'tcsh' does not provide an obvious way to redirect standard error, but not standard out. Use
the following trick to do so:
(ls -al filename > /dev/tty) >& list.err
Unix_tricks.txt Page 74 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
No Clobber
If you wish to avoid accidentally overwriting files with redirection, set the 'tcsh' 'noclobber'
variable. For example:
% set noclobber
% ls
.....list list.err ....
% ls > list
list: File exists.
% ls >! list
%
Notice the use of '!' to temporarily override 'noclobber'.
Killing Time
To kill an application (or process) from the command line, we first need to find its Process ID
or PID. For example:
% ps -axww | grep -i clock
15048 ?? S 0:00.65 /Applications/Clock.app/Contents/MacOS/Clock
15054 std R+ 0:00.00 grep -i clock
and then issue the 'kill' command:
% kill 15048
Note the use of option 'ww' which ensures that the output from 'ps' is not truncated losing the
name we are grepping for.
A PID of -1 broadcasts the signal to all process for which you have the necessary permissions to
kill.
Alternatively, we can use:
killall Clock
which kills by (case sensitive) name. Be careful as this command kills all process that match the
given name.
The '-m' option of 'killall' takes a regular expression as the process name, and kills all those
processes that match it.
Many Ways to Kill
The 'kill' and 'killall' commands send a 'TERM' signal to the process, which is a request to that
process to terminate.
Other signals are available, including
-KILL - make the process an offer it can't refuse. This always kills the process.
-HUP - tell the process to restart. This is useful to restart demons (like configd and lookupd),
as they will re-read their configuration files on receipt of a -hup signal.
Unix_tricks.txt Page 75 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The command:
% kill -l
lists all the available signals, and:
% man sigaction
gives more detail.
The Killing Script
Here is a handy script to kill processes by name:
% cat killer
#!/bin/sh
kill $(ps auxww | grep -i $1 | grep -v $1 | awk '{print $2}')
% killer clock
*gone!*
Remember to make the script executable.
Find Your Victim
Many Unix demons kindly leave their address. Check out:
% ls /var/run/
AppleFileServer.pid cron.pid mDNSResponder.pid ntp.drift sshd.pid xinetd.pid
StartupItems davlocks named.pid ntpd.pid sudo
autodiskmount.pid httpd.pid ndc pppconfd syslog
automount.pid inetd.pid netinfo_local.pid proxy syslog.pid
configd.pid lookupd.pid niconfig_local.xml resolv.conf utmp
These should be independent of the name of the demon. For example, www servers should report
their pid at 'httpd.pid'.
You can make a handy alias to restart a demon, such as:
% alias nfs-mount 'sudo kill -HUP `cat /var/run/automount.pid`'
% alias nfs-export 'sudo kill -HUP `cat /var/run/mountd.pid`'
which will restart the NFS client and server respectively after new shares and connections have
been added.
The Hanging Script
Here is a handy script to restart a demon:
% cat hanger
#!/bin/sh
if [ "$1" = "" ]; then
echo "List of demons:"
cd /var/run
ls -1 *.pid | sed -e 's/\.pid//'
else
echo "restarting demon $1"
sudo kill -HUP `cat /var/run/$1.pid`
fi
It will list the demons:
Unix_tricks.txt Page 76 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
~ % hanger
List of demons:
AppleFileServer
autodiskmount
automount
configd
cron
httpd
inetd
lookupd
mDNSResponder
named
netinfo_local
ntpd
sshd
syslog
xinetd
and kill them:
% hanger lookupd
restarting demon lookupd
Remember to make the script executable.
Start and Stop Apache
To start and stop apache from the command line use the Apache control utility:
% sudo apachectl start
/usr/sbin/apachectl start: httpd started
% sudo apachectl stop
/usr/sbin/apachectl stop: httpd stopped
To cause Apache to be started when your Mac boots, edit the file:
/etc/hostconfig
as root, and change
WEBSERVER=-NO-
to
WEBSERVER=-YES-
Restart Apache
If you change Apache's configuration files, you must restart Apache to cause it to re-read them.
To do this gracefully (without disturbing existing connections on a live server) use:
% sudo apachectl graceful
/usr/sbin/apachectl graceful: httpd gracefully restarted
Change WebServer Root
By default, Apache on Mac Os X servers from /Library/WebServer/Documents. If you wish to change
this to another directory, like ~/Sites, edit Apache's configuration file:
% sudo pico /etc/httpd/httpd.conf
Replace this line (line 363?):
Unix_tricks.txt Page 77 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
DocumentRoot "/Library/WebServer/Documents"
with:
DocumentRoot "/Users/your-user-name-here/Sites"
and this line (line 388?):
<Directory "/Library/WebServer/Documents">
with:
<Directory "/Users/your-user-name-here/Sites">
The Apache Manual
Access the Apache on-line manual with the URL: http://localhost/manual/
The Apache Log Files
Apache writes logging information to:
/private/var/log/httpd/error_log
and:
/private/var/log/httpd/access_log
Examine the error log if you experience problems in setting up the server. Examine the access log
to see who is viewing your site.
You can change the amount of logging information written to these files. For example, to add the
referrer (the site which linked to your site) and the user agent (the browser) to the log file,
do the following.
Edit the apache configuration file:
% sudo pico /etc/httpd/httpd.conf
Search for:
CustomLog "/private/var/log/httpd/access_log" common
and replace with:
CustomLog "/private/var/log/httpd/access_log" combined
List the Shell Built-ins
The tcsh shell has many useful built-in commands (commands that are part of the shell and do not
live as separate executables in /bin etc).
To list all builtins:
% builtins
: @ alias alloc bg bindkey
break breaksw builtins case cd chdir
...
If the same command exists as both a separate executable and a shell built-in, the shell built-in
will be executed. To override this, precede the command with '\'.
% which which
which: shell built-in command.
% which \which
/usr/bin/which
Unix_tricks.txt Page 78 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
and try this:
% \which which
The Command Hash Table
When the shell is given a command to execute, it will search its own built-in commands first,
then all the directories specified in the PATH or path environment/shell variables. To see the
path, issue either of:
% echo $PATH
% echo $path
In order to speed up this search process, the shell searches these directories when it first
starts, and caches the information in memory. If you add a new command or shell script to one of
these directories, the shell will not recognize it. To get the shell to refresh its cache issue
the built-in command 'rehash'
% rehash
No response means all is ok.
Try this:
% hashstat
umask
The umask built-in command tells the file system the default permissions to apply to newly
created files and directories. The umask can be set in your tcsh startup file, or applied just to
a partiaular Terminal session.
Without a 'umask' directories are set to:
drwxrwxrwx 2 saruman staff 68 Jun 9 11:16 test-dir
and files to:
-rw-rw-rw- 1 saruman staff 0 Jun 9 11:16 test-file
The umask is usually set to 022:
% umask 022
% touch test-file
% ls -al test-file
-rw-r--r-- 1 saruman staff 0 Jun 9 11:22 test-file
Give write access to your group:
% rm test-file
% umask 002
% touch test-file
% ls -al test-file
-rw-rw-r-- 1 saruman staff 0 Jun 9 11:23 test-file
For shared files:
% rm test-file
% umask 000
% touch test-file
% ls -al test-file
-rw-rw-rw- 1 saruman staff 0 Jun 9 11:23 test-file
Unix_tricks.txt Page 79 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
First off, apologies for the lack of tips last week - I'm fit and well again for this week.
When the 'tcsh' shell starts up it reads and executes several script files. These you are free to
change (or create) if you wish to customise your own command line environment. They are:
/etc/csh.cshrc
/etc/csh.login
~/.tcshrc
~/.login
You may add Unix and 'tcsh' shell commands to these files. The first two files are global to all
users, and the second two are per-user.
tcshrc and cshrc
For backward compatibility with the 'csh' shell (from which the 'tcsh' shell was derived), the
script file:
~/.cshrc
may or may not be executed when the 'tcsh' shell starts up.
In the absence of '~/.tcshrc', '~/.cshrc' is executed. Once you create '~/.tcshrc', '~/.cshrc' is
no longer executed. Therefore, never rely on having both of these files executed. Choose one,
preferably '~/.tcshrc', and stick to it even if you are advised to add lines to the other. The
two are interchangeable.
Login and Interactive Shells
What are login and interactive shells?
Opening a Terminal window starts an interactive login shell.
Executing a shell script starts a non-interactive non-login shell.
Typing 'tcsh' at the command line spawns an interactive non-login shell.
Login shells differ in that they read and execute:
/etc/csh.login
~/.login
whereas non-login shells do not.
Interactive shells are attached to a Terminal window so you can type commands into them.
Non-interactive shells take their commands from a script.
Some terminals (such as xterm that runs in X11) by default do not start a login shell. To get
xterm to do so, and therefore behave more like Apple's Terminal, add this to ~/.Xdefaults:
# xterm
XTerm*.LoginShell: True
Login-only Commands
Optimize the shell startup sequence by using the login-shell-only startup files:
/etc/csh.login
~/.login
In these files set up aspects of the environment that persist, such as environment variables.
Unix_tricks.txt Page 81 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
'xargs' vs '-exec'
Referring back to Monday's tip...
% find . -iname "*.ws" -exec grep -il warning {} \;
versus:
% find . -iname "*.ws" | xargs grep -il warning
...some may tell you that piping to 'xargs' is less efficient than using the built-in '-exec'
option. This is not so, and in fact the latter example will run at least ten times as fast as the
former.
Why?
Because '-exec' will execute the 'grep' command once for each file found . In this example, over
200 files matched "*.ws". 'xargs' on the other hand absorbs all the arguments and passes them to
'grep' in one go.
'xarg' option
Monday's example...
% find . -iname "*.ws" | xargs grep -il warning
...works because 'grep' can take a long list of files to process. If a command takes only one
file at a time, use option '-n1'.
% find . -iname "*.ws" | xargs -n1 my-simple-script
Option '-t' is useful (trace) - it and causes 'xargs' to echo to standard output the command it
has formed and will execute.
% find . -iname "*.ws" | xargs grep -t -il warning
grep -il warning ./index.ws ./week1/friday.ws ./week1/monday.ws
./week1/thursday.ws ./week1/tuesday.ws ./week1/wednesday.ws
./week10/friday.ws ./week10/monday.ws ./week10/thursday.ws
...
./week8/friday.ws ./week8/monday.ws ./week8/thursday.ws
./week8/tuesday.ws ./week8/wednesday.ws ./week9/friday.ws
./week9/monday.ws ./week9/thursday.ws ./week9/tuesday.ws
./week9/wednesday.ws
./week13/thursday.ws
./week21/thursday.ws
./week5/friday.ws
./week5/thursday.ws
./week9/monday.ws
Too Many Files?
If 'find' finds many, many files, the resulting command line formed by 'xargs' may be too long.
Limit this by using option '-n' which states the maximum number of parameters that 'xargs' will
pass to the command (in this example to 'grep')
% find . -iname "*.ws" | xargs -n100 grep -il warning
...causes 'xargs' to call 'grep' once it has 100 filenames. If 350 filenames were found, 'xargs'
would call 'grep' four times.
Unix_tricks.txt Page 83 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
How do I find the total size of all Photoshop files (*.psd) in my home directory?
% find ~ -iname "*.psd" -print0 | xargs -0 du -ch
15M ./Pictures/complete/ferdi-coll3.psd
30M ./Pictures/complete/ferdi-cool.psd
1.7M ./Pictures/complete/ferdi-gala.psd
...
600k ./Pictures/web-site/misc/web5.psd
72k ./Pictures/web-site/osxfaq/banner1.psd
48k ./Pictures/web-site/osxfaq/banner2.psd
96k ./Pictures/web-site/osxfaq/btn-.psd
28k ./Pictures/web-site/osxfaq/end.psd
316M total
There! - 316M.
The Mac OS X standard 'du' command does not understand option '-h' (human readable output). Use
-k instead to see file sizes in K-bytes.
GNU package 'fileutils' contains superior versions of many basic commands, including 'du' and a
colour version of 'ls'. It is most easily got using Fink.
...Seek Out Large Directories
How do I report on all Directories who's contents exceed 100 Meg?
Previous week's tips have covered a similar question to report on all individual files that
exceed a given size. For example, find all files that exceed 20 Meg:
% find . -size +40000
To apply this to directories use:
% du -k ~ | egrep "^[0-9]{6,}"
139252 /Users/saruman/Library
267020 /Users/saruman/Pictures/complete
357432 /Users/saruman/Pictures/iPhoto Library/2001
214084 /Users/saruman/Pictures/iPhoto Library/2002
195788 /Users/saruman/Pictures/iPhoto Library/2003/01
124948 /Users/saruman/Pictures/iPhoto Library/2003/05
149976 /Users/saruman/Pictures/iPhoto Library/2003/06
545460 /Users/saruman/Pictures/iPhoto Library/2003
1149224 /Users/saruman/Pictures/iPhoto Library
1548924 /Users/saruman/Pictures
1902804 /Users/saruman
This reports on all directories who size in K-bytes is six digits or more (controlled by {6,}).
...Delete Blank Lines
How do I delete empty lines from a file?
Using 'sed':
% sed '/^$/d' in-file > out-file
Using 'tr':
% tr -s \\n < in-file > out-file
If the file has blank lines containing spaces:
% sed '/^[ ]*$/d' in-file > out-file
Unix_tricks.txt Page 86 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
If the blank lines have tabs too:
% sed '/^[ \t]*$/d' in-file > out-file
BUT, OS X 'sed' command does not seem to understand '\t' as meaning tab.
...Treat a Command as a Function
This addresses a generic question, best illustrated by an example.
The command 'tr' translates or strips characters from the contents of a file. Like most commands,
it reads and writes files, or standard IO. For example, to strip spaces use:
% tr -d " " < in-file > out-file
How can we use a command like 'tr' in a shell script, to strip spaces from the contents of a
variable? The effect we want is as if we could say:
var = tr ($var);
The secret is to stream the value of the input parameter, and capture the output stream, thus:
In tcsh use:
set var=`echo $var | tr -d " "`
In sh use:
var=$( echo $var | tr -d " " )
...Remove Spaces from Filenames
How can I eliminate spaces in all my filenames?
This 'sh' shell script will do the trick, providing no two filenames in a directory differ by
only spaces, and no directory names have spaces.
% cat sh2
#!/bin/sh
target_fn=$( echo $1 | tr -d " " )
if [ ! "$1" = "$target_fn" ]; then
echo mv "$1" "$target_fn"
mv "$1" "$target_fn"
fi
% ls -R1
.:
dirname
file name
file2 name
sh
sh2
tsh
dirname:
file2 name
file3name
% find . -print0 | xargs -0 -n1 ./sh2
Unix_tricks.txt Page 87 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Find Files of a Given Type
How do I locate all Bourne shell scripts, or all text files, in my home directory?
We all know how to use 'find' to do this based on file extension. But what about file contents,
which was what was required for this question.
Use the 'file' command, combined with 'find' and 'grep':
% find . -print0 | xargs -0 file | egrep "(Bourne|Tenex)"
./sh: Bourne shell script text
./sh2: Bourne shell script text
./tsh: Tenex C shell script text
will find all 'sh' and 'tcsh' scripts. The magic is specified by /etc/magic.
Restart a Daemon
Week 45, Friday gave a script to do just this. This doesn't always work however, because some
cheeky little daemons write additional information to their .pid file:
% sudo cat /var/run/sendmail.pid
11024
/usr/sbin/sendmail -L mta -bd -q30m
Change the script to use 'head' instead of 'cat', so as to cut the additional lines:
...
sudo kill -HUP `head -n1 /var/run/$1.pid`
...
Edit Many Files with vim
Use the 'new' command to split the screen to edit many files in the same Terminal window.
:new
splits the screen in half and creates a new edit buffer, and:
:new file-1
splits and reads 'file-1' into the new edit buffer.
Use ctrl-WW (or ctrl-W ctrl-W) to toggle between buffers/files.
Vertically Split Screen in Vim
Vim can split a window vertically too, which is useful for comparing two files side by side.
:vert new [file-1]
will split the screen vertically and optionally read file-1 into the second buffer.
Diffing Two Files with Vim
The command:
% vimdiff file-1 file-2
will start vim with two files, create a vertical split, and mark all differences between the
files.
Unix_tricks.txt Page 89 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Alternatively, if you are editing file-1 already, use:
:diffsplit file-2
or
:vert diffsplit file-2
to load file-2 and view the differences.
Batch Edit Many Files with Vim
The command:
% vim f1 f2 f3 f4 f5
will cause vim to read the five named files, and start editing f1.
To move between files use:
:n - next file in list
:prev - previous file in list
:wn or :wprev is necessary to write out changes and then edit the next file, if the current file
has changed.
List the buffers with:
:buffers
and jump to a specific buffer (eg buffer 2) with:
:b2
Vim Shortcuts
For those familiar with vim shorcuts, here are some that can be added to your .vimrc file.
Submitted by Mike Schienle
" typos
ab teh the
ab pritn print
" key maps
" these rely on blocks marked as a (begin) and b (end)
" \D delete everything between marks a and b
map \D :'a,'bd<CR>
" backslash P - put buffer a at current cursor location
map \P :pu a<CR>
" backslash Y - yank a-b into buffer a
map \Y :'a,'by a<CR>
" star c - convert to lowercase
map *c :'a,'bs/\<[A-Z]/\l&/g<CR>
Unix_tricks.txt Page 90 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
" star C - convert to uppercase
map *C :'a,'bs/\<[a-z]/\u&/g<CR>
Setting Variables and Environment Variables
This week's tips compare how some common tasks look in tcsh versus sh/bash. This is a handy
reference if, like me, you are continually flipping between the two and get the syntaxes mixed
up.
How to set variables and environment variables:
tcsh sh/bash
set var = value % var=value
setenv ENV value % ENV=value; export ENV
In bash (additional to sh) variables can be declared and marked as exported and/or read only:
declare [-options] var=value
-r is read only
-x is exported (environment variable)
% declare -rx ENV=cant_change
Redirection and Pipes
For tcsh:
stdout cmd >fn
stderr (>/dev/tty) >&fn2
both cmd >& fn
both diff files (cmd >fn) >&fn2
stdin cmd <fn
to append >>
no clobber >!
pipe stdout cmd | cmd2
pipe both cmd |& cmd2
For sh/bash:
stdout cmd >fn
stderr cmd 2>fn
both same file cmd >fn 2>&1 (cmd &>fn - bash)
both diff files cmd >fn 2>fn2
stdin
to append >>fn
no clobber >|fn
pipe stdout cmd | cmd2
pipe both cmd 2>&1 | cmd2
Unix_tricks.txt Page 91 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
close stdout cmd >&-
close stderr cmd 2>&-
close stdin cmd <&-
open file cmd <>fn
Startup Files
The following files are executed when the shell starts up in interactive mode, and in the order
given. Both login and non-login shell startups are given.
tcsh login shell:
% tcsh -l
/etc/csh.cshrc
/etc/csh.login
~/.tcshrc
~/.login
bash login shell:
% bash --login
/etc/profile
~/.bash_profile
tcsh non-login shell:
% tcsh
/etc/csh.cshrc
~/.tcshrc
bash non-login shell:
% bash
~/.bashrc
Control Constructs 1 (Choice)
The scripting language of tcsh looks very much like the 'c' programming language (hence the 'c'
in tcsh) while sh/bash does not.
'if' construct:
#!/bin/tcsh
if ("$1" == "positive") then
echo "Yes"
else if ("$1" == "negative") then
echo "No"
else
echo "Not sure"
endif
#!/bin/sh
if [ "$1" = "positive" ]; then
echo "Yes"
elif [ "$1" = "negative" ]; then
echo "No"
else
echo "Not sure"
Unix_tricks.txt Page 92 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
fi
'switch/case' construct:
#!/bin/tcsh
switch ("$1")
case "positive":
echo "Yes"
breaksw
case "negative":
echo "No"
breaksw
default:
echo "Not sure"
breaksw
endsw
#!/bin/sh
case "$1" in
"positive")
echo "Yes"
;;
"negative")
echo "No"
;;
*)
echo "Not sure"
;;
esac
Control Constructs 2 (Looping)
The scripting language of tcsh looks very much like the 'c' programming language (hence the 'c'
in tcsh) while sh/bash does not.
'for' loops:
#!/bin/tcsh
foreach word (hello goodbye au-revoir)
echo $word
end
#!/bin/sh
for word in hello goodbye au-revoir
do
echo $word
done
Unix_tricks.txt Page 93 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
'while' loops:
#!/bin/sh
n=0
while [ ! $n = 10 ]
do
echo $n
n=$(expr $n + 1)
done
#!/bin/tcsh
set n = 0
while ($n != 10)
echo $n
set n = `expr $n + 1`
end
Oddly-Named Files
If you have a filename that shows up like:
% ls Z* Z???
It may contain non-standard characters. Use tabbed completion to reveal the filename:
% ls Z<hit tab key>
% ls Zb^D"
(Note that the filenames don't appear so odd by the time they reach your browser.)
The file can be renamed with:
% mv Z<hit tab key>
% mv Zb^D" z-file
If the type-able portion of the file in not unique:
% ls d<hit tab key>
DB)b^D"o#?b^B, DB4#^ Danger
If the type-able portion of the file in not unique:
% ls d<hit tab key>
DB)b^D"o#?b^B, DB4#^ Danger
use the inode number to identify the file.
% ls -i
590795 Danger 579654 bvt 563546 page2.htm 578980 tvb
590442 D??????????? 560810 Cantata.txt 563547 page3.HTML 529632 users
590441 D??#^
...
then use 'find' to rename it:
% find . -inum 590442 -exec mv {} D-file \;
'fsck' it
Sometimes, repairing the disk can help make odd ghostly files a little more solid.
To do this boot the machine into single user mode by holding down command (apple)-S.
Unix_tricks.txt Page 94 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
% sudo rm -ri ~/.Trash
This will zap all files except those that are immutable or in use.
'sudo' executes the command as root and therefore overrides all permissions
'rm' removes files
'-r' does it recursively - ie to all nested files and directories
'-i' is confirm mode - you will be prompted for each file to delete, and I suggest to use this as
a safety precaution. If you have many, many files tedium may set in, so you can replace the '-i'
with '-f' (force).
2 - Immutable files
You can check which, if any, files have the immutable flag set by specifying the '-o' option to
'ls'.
ls -alo
drwx------ 15 saruman staff - 510 Jan 6 23:35 osxfaq
-rw-r--r-- 1 saruman staff uchg 0 Feb 20 14:04 user-imutable-set
'uchg' indicates that the User Immutable Flag is set. 'schg' indicates that the System Immutable
Flag is set (see point 4 below).
Clear the immutable flag of all files, then delete:
% sudo chflags -R nouchg ~/.Trash
% sudo rm -ri ~/.Trash
'chflags' changes a file's flags settings.
'nouchg' clears the user immutable flag ('uchg' would set it)
'-R' does it recursively.
3 - Open Files
You can determine which files in the trash are open, and which application has them open, with
the 'fstat' command:
% ls -al ~/.Trash
total 0
drwx------ 5 melkor staff 126 Jun 25 16:43 .
drwxr-xr-x 24 melkor staff 772 Jun 25 16:32 ..
-rw-r--r-- 1 melkor staff 0 Jun 25 16:32 xxx
-rw-r--r-- 1 melkor staff 0 Jun 25 16:43 yyy
-rw-r--r-- 1 melkor staff 0 Jun 25 16:43 zzz
% fstat ~/.Trash/*
USER .CMD PID FD INUM MODE SZ|DV R/W MOUNT NAME
melkor vi 731 3 368860 -rw-r--r-- 0 r / /Users/melkor/.Trash/xxx
...that's the one - 'vi' is editing 'xxx'...
Close the offending files, then attempt to empty the trash again.
If you are unable to close all open files, resort to the method described in the GUI approach.
Tell Me More...
warning
Unix_tricks.txt Page 96 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
sudo
sudo (super user do) is a Unix command that allows you to run another command as root. You must
enter your admin password (not the root password) when prompted.
A user without administrative privileges cannot use 'sudo'
If you were to accidentally put a space between '.' and 'Trash' :
%rm -rf ~/. Trash
'rm' would apply to both '~/.' (your home directory) and 'Trash' ( a non-existent directory). You
would wipe every single file from your home directory. Oops!
That's one reason why I suggest the '-i' option
% rm -ri ~/.Trash
so you get a prompt for each file. Hopefully you will spot your mistake on the first confirmation
and avoid loosing anything
4 - The Nightmare Scenario
If all of the above methods fail, the offending files my have the System Immutable flag set. Such
files are locked Fort Knox style. The System Immutable flag cannot be changed by root. Only by
super-root - the super-duper-user. What!?!
When your Mac is up and running in multi-user mode (the normal operating mode) it is running at
level 1. Some operations even root can't do at level 1, such as turn off the System Immutable
flag. You must run at level 0. Switching into single user mode will allow one to run at level 0
and thus change the System Immutable flag.
Do this.
Close all applications and issue the command:
% sudo shutdown +0 "Bye bye"
to shutdown multi-user mode and enter single user mode. You will lose all services such as
network connectivity while in single user mode. Alternatively, you can reboot your Mac, then
startup with both the Command (Apple) key and the 's' key held down.
% cd /Users/your-name-here/.Trash
% chflags -R noschg *
Then hit control-d to return to multi-user mode. You should now be able to delete the trash with
the Finder, or by reapplying the steps above.
Note: when you enter single user mode, type:
% whoami
if the answer is not root type:
% su
and type control-d twice when you exit.
Other Trashes
Unix_tricks.txt Page 97 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
On some occasions, trash is placed at the root level of your system disc or another partition or
drive. The directory is:
/.Trashes/501
and
/Volumes/disc-name-here/.Trashes/501
See if they are empty by listing them.
% sudo ls -al /.Trashes/501
...
'501' is the User ID of the user who did the trashing.
% sudo ls -al /.Trashes
may reveal several such 50x directories.
You can check your own User ID (uid) by issuing the command:
% id
and substitute this if it is not 501.
You can then apply all the techniques given for ~/.Trash to /.Trashes/501.
And Finally
If you still have problems, and you have re-applied the above steps as necessary, you probably
have a corrupt file system.
Firstly, enter single user mode and issue:
% fsck
and answer the prompts, or else have 'fsck' run automatically with:
% fsck -y
You can also try commercial disc repair programs.
The Executable Name
This week's tips enhance the Learning Centre tutorial on scripting, part 8 and part 9.
Note that these tips are for 'sh' or 'bash' scripts.
The shell special parameter $0 contains the script name, but includes the full path name. Display
just the script name using ${0##*/}
$ cat usage
#!/bin/sh
echo "Usage 1: $0"
echo "Usage 2: ${0##*/}"
$ ~/Development/test/usage
Usage 1: /Users/saruman/Development/test/usage
Unix_tricks.txt Page 98 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Usage 2: usage
Complex Conditions
Conditions in 'if', 'elif', and 'while' statements can contain AND, OR, and NOT elements. For
example, to test if parameter 1 is "-p" AND parameter 2 is empty use:
if [ "$1" = "-p" -a "$2" = "" ]; then
The '-a' represents AND.
Use '-o' for OR.
To reverse a condition use pling (!). For example to test if a file is NOT a directory use:
if [ ! -d "$file" ]; then
Check if a Volume is Mounted
To check if a volume is mounted, for example /Volumes/your-user, it is necessary to check that
/Volumes/your-user exists AND is a mount, not a regular file or directory. One way to do this is
to examine the output from df:
mounted=$(df | grep "/Volumes/$USER")
If the volume is not mounted then the variable 'mounted' will be a null string. Note that the
environment variable $USER expands to the short name of the current user.
One can use the condition:
if [ ! "$mounted" ]; then
statement to mount /Volumes/$USER
fi
because an empty string has the test value FALSE.
Mounting and AFP Volume
An AppleShare volume can be mounted using the lines:
mkdir /Volumes/$USER
mount_afp afp://$USER@carcharoth.mayo-family.com/$USER /Volumes/$USER > /dev/null
This mounts the home directory of the current user that is located on the server 'SERVER' at
mount point /Volumes/users-short-name.
Eg, the script:
$ cat mount-user
#!/bin/sh
mounted=$(df | grep "/Volumes/$USER")
if [ ! "$mounted" ]; then
echo "Mounting /Volumes/$USER"
mkdir /Volumes/$USER
mount_afp afp://$USER@your-server-address-here/$USER /Volumes/$USER > /dev/null
else
echo "/Volumes/$USER already mounted"
Unix_tricks.txt Page 99 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
fi
Will mount my home directory (saruman) located on my server under /Volumes/saruman:
$ ls /Volumes
<empty>
$ ./mount-user
Mounting /Volumes/saruman
$ ls /Volumes
saruman
$ ./mount
/Volumes/saruman already mounted
A Mount-User Script
Here is a script using the tips given this week, and the shell programming tutorials in the
Learning Centre part8 and part 9.
It mounts the current user's home directory located on the server $SERVER, on the local machine
at mount point $AFP_MOUNT/$USER.
It uses two environment variables which must be set before the script runs:
AFP_MOUNT to say where to mount the volumes (this is usually '/Volumes')
SERVER for the network address of the server
A password can optionally be given with '-p password'. Otherwise mount_afp will ask for your
password in a pop-up, or extract it from the keychain.
$ cat mount-user
#!/bin/sh
# mounts the current user's home directory on the server into the afp mount dir
#
if [ ! "$1" = "" -a ! "$1" = "-p" ]; then
echo "Usage: ${0##*/} [-p password]"
echo " mounts ${USER}'s home directory on the server at $AFP_MOUNT/$USER"
exit
fi
if [ "$1" = "-p" -a "$2" = "" ]; then
echo "Usage: ${0##*/} [-p password]"
echo " give a password"
exit
fi
# if password is given add ":" to the start to comply with mount syntax
if [ "$1" = "" ]; then
pass=""
else
pass=":"$2
fi
Unix_tricks.txt Page 100 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# check if already mounted
#
mounted=$(df | grep "$AFP_MOUNT/$USER")
# if not mounted, ensure the mount point (directory) is available
#
if [ ! "$mounted" ]; then
if [ ! -d $AFP_MOUNT/$USER ]; then
if [ -e $AFP_MOUNT/$USER ]; then
rm -f $AFP_MOUNT/$USER
fi
mkdir $AFP_MOUNT/$USER
fi
mount_afp afp://$USER$pass@$SERVER/$USER $ALM_AFP_MOUNT/$USER > /dev/null
disktool -r
fi
# return 0 if no mount was performed, 1 otherwise
#
if [ ! "$mounted" ]; then
exit 1
else
exit 0
fi
Symbolic Links
A link is a file that 'points' to another file. They come in two varieties: symbolic link and
hard link (covered on Tuesday).
A symbolic link is created using the 'ln' command, specifying option '-s' for symbolic.
% echo "I am the original file" > orig-file
% /bin/ls -l *-file
lrwxr-xr-x 1 saruman staff 9 ... link-file -> orig-file
-rw-r--r-- 2 saruman staff 23 ... orig-file
The symbolic link is shown with the first character as 'l' in a long listing, and as pointing to
the original file.
% cat orig-file
I am the original file
% cat link-file
I am the original file
'link-file' is simply a file that contains some text describing the absolute path to 'orig-file',
and has a flag set to say 'I am a symbolic link'. The two files can be anywhere on the file
system - different directories, partitions, disks, or machines.
When 'link-file' is accessed, the link is automatically followed resulting in 'orig-file' being
accessed.
If 'orig-file' is moved or renamed the link will be broken.
Unix_tricks.txt Page 101 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Hard Links
A hard link is created using the 'ln' command, but WITHOUT specifying option '-s'.
% ln orig-file hard-file
% /bin/ls -l *-file
-rw-r--r-- 2 saruman staff 23 ... hard-file
lrwxr-xr-x 1 saruman staff 9 ... link-file -> orig-file
-rw-r--r-- 2 saruman staff 23 ... orig-file
% cat orig-file
I am the original file
% cat hard-file
I am the original file
'hard-file' is not a new file, but a new directory entry that points to the original file's data
(the same part of the disc). In technical terms, the two directory entries reference the same
inode.
The two files are one in the same, and it is not possible to determine which is the original and
which is the hard link.
Either file can be renamed or moved without breaking the link.
Hard links cannot be created across partitions or disks. This is because inodes belong to a
particular partition and are not unique across partitions or drives.
Aliases
An alias is managed by the Mac Finder and is not recognized by Unix. An alias has an empty data
fork, with the aliasing information stored in the resource fork.
(Create and alias (alias-file) to orig-file using the Finder.)
% /bin/ls -l *-file
-rw-r--r-- 1 saruman staff 0 ... alias-file
-rw-r--r-- 2 saruman staff 23 ... hard-file
lrwxr-xr-x 1 saruman staff 9 ... link-file > orig-file
-rw-r--r-- 2 saruman staff 23 ... orig-file
% cat orig-file
I am the original file
% cat alias-file
%
Unix sees only the data fork and hence 'alias-file' as being empty. The Finder recognizes
symbolic and hard links. Copying an alias using the Unix 'cp' command will create only an empty
file.
Note that 'ls' with option -s does recognize the resource fork when calculating the number of
blocks the files uses (36k or 72 half-K blocks).
% /bin/ls -sl *-file
72 -rw-r--r-- 1 saruman staff 0 ... alias-file
8 -rw-r--r-- 2 saruman staff 23 ... hard-file
8 lrwxr-xr-x 1 saruman staff 9 ... link-file > orig-file
8 -rw-r--r-- 2 saruman staff 23 ... orig-file
Unix_tricks.txt Page 102 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Working with Links
Many commands take options to affect the way they treat a link - usually to control if they deal
with the link itself, or follow the link and deal with the target file. This applies only to
symbolic links.
'ls' can be told to follow the link with option '-L':
% ln -s ~/Documents/Letters/Channel\ Dynamics a-letter
% /bin/ls -l a-letter
lrwxr-xr-x 1 saruman staff 49 ... a-letter >
/Users/saruman/Documents/Letters/Channel Dynamics
% /bin/ls -lL a-letter
-rw-r--r-- 1 saruman staff 20480 ... a-letter
and can show inode numbers with option '-i':
% ls -l i *-file
678102 -rw-r--r-- 1 saruman staff 0 ... alias-file
678094 -rw-r--r-- 2 saruman staff 23 ... hard-file
678095 lrwxr-xr-x 1 saruman staff 9 ... link-file > orig-file
678094 -rw-r--r-- 2 saruman staff 23 ... orig-file
Note that the hard link and original file have the same i-node number.
Other commands take options '-H', '-L', and '-R' which generally mean:
-H If the -R option is specified, symbolic links on the
command line are followed. (Symbolic links encountered in
the tree traversal are not followed.)
-L If the -R option is specified, all symbolic links are
followed.
-P If the -R option is specified, no symbolic links are
followed.
The commands include:
* 'chflags'
* 'chgrp'
* 'chmod'
* 'chown'
* 'cp' HLP
* 'du' HLP
* 'find'
'test' (aka '[') can test for a symbolic link with '-L':
-L file - True if file exists and is a symbolic link.
Making a Shadow Directory
The command 'lndir' creates a shadow directory of symbolic links to another directory.
Unix_tricks.txt Page 103 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
following.
For example to search for all text files (*.txt) that contain the word "memo", and then edit them
using 'vim':
vim `find . -type f -iname "*.txt" -print0 | xargs -0 grep -ilw "memo"`
* The -print0 and -0 options are to cope with filenames containing spaces.
* 'Find' option -type f is to ensure only regular files are considered.
* 'Grep' options -iw ensure the text search is case insensitive and matches only whole words.
Less (and More) Edits
If you view a file with 'less', then decide you wish to edit that file, just hit 'v'. This will
take you into the 'vi' editor, pre-loaded with the file you were viewing.
If 'vi' is not to your taste, set the environment variable VISUAL to define which editor will be
used.
% setenv VISUAL vim
You can do the same trick with 'more', except that you will need to set environment variable
EDITOR instead. Note that 'less' will use EDITOR if VISUAL is not defined.
Corruption...
If your terminal session gets corrupted, for example after accidentally editing or displaying a
binary file, use 'tput'.
% tput reset
to reset the Terminal, or
% tput init
to initialise it.
The difference between these two depends on the difference between the 'reset' and 'init'
terminal strings in the 'terminfo' database (/usr/share/terminfo/....). Mac OS X uses the
terminfo rather than termcap. Terminfo is a compiled database, and in order to view the
information you must use 'infocmp terminal-name'.
For example:
% infocmp vt100
% infocmp xterm
Cleaning Up
Tidy up a file with 'tr' and 'sed'.
The following command removes duplicate spaces, blank lines, and changes Mac style end of lines
into Unix style.
% tr -s " " < test.txt | tr \\r \\n | sed '/^$/d' > out.txt
The input file is test.txt and the output is out.txt
About POSIX Regular Expressions
REs come in three flavours: older style 'basic' or 'obsolete' as recognized by 'grep', 'sed',
Unix_tricks.txt Page 105 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
'vi', ...; 'extended' or POSIX-compliant as recognized by egrep, gawk, PHP, ...; and Perl regular
expressions as recognised by Perl and PHP.
Perl are more powerful than POSIX, which in turn are more powerful than basic.
Incompatibilities exist between the different varieties, so one should check which form to use
with any given utility.
By default, searching is done a line at a time, and is case sensitive.
This week's tips will concentrate on POSIX-style REs.
Matching and Escaping with POSIX Regular Expressions
% cat test
This is a test file
to illustrate the use of
regular expressions.
Here is a dollar $ symbol.
this is the end of the test file.
To search for a simple string:
% egrep "This" test
This is a test file
You must explicitly make the search case-insensitive: the method will depend on the utility you
are using. For 'egrep' use option -i.
Characters that have a special meaning within regular expressions must be escaped to say "take
the character literally and ignore any special meaning it may otherwise have".
Special characters that must be escaped are:
. ^ $ * + ? [ ( ) | { \
For example:
% egrep 'dollar $' test
(no matches output)
% egrep 'dollar \$' test
Here is a dollar $ symbol.
Use single quotes to prevent the shell from interpreting the $ sign.
Note that it is not always necessary to escape special characters because they may be special
only in a particular context, but to be safe it won't harm to always escape.
% egrep 'dollar $ symbol' test
Here is a dollar $ symbol.
Anchors in POSIX Regular Expressions
An anchor is used to force the pattern to match either the start or end of a line.
% cat test
this is line one
and this is the second line
Searching for 'this' or 'line' will match both lines of the file:
Use '^' to anchor the pattern to the start of a line:
% egrep '^this' test
Unix_tricks.txt Page 106 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
matches bbg, bcg, ... bzg, but not bag, beg, etc.
Match a range of consecutive characters using a hyphen, like [a-z] for all lower-case letters, or
[0-9] for all decimal digits. For example:
% egrep '^[a-zA-Z]{3}[0-9]{2}$' file
matches 'letter letter letter digit digit' anchored to the start and end of the line (see last
week's tips for anchors and {} repeaters).
Character Classes in Regular Expressions
A character class is a name for a particular set of characters, is enclosed in :class-name: and
used within a bracketed expression.
[:alpha:] matches any alphabetic character
[:alnum:] matches any alphanumeric character
[:alpha:?] matches any alphabetic character or a '?'.
The classes are:
alnum digit punct
alpha graph space
blank lower upper
cntrl print xdigit
Character classes work in PHP and Perl, but don't seem to work in egrep in OS X 10.2.
Escaping, or Lack Thereof
It is not necessary (in fact it is incorrect) to escape the special characters ^ $ * + ? [ ( ) |
{ \ within a bracketed expression (see Monday's tip).
To match a '^' ensure it is *not* the first character in the list (otherwise it will invert the
match), and to match ']' ensure it *is* the first character in the list.
% egrep '[]%$^]' file
matches any of ] % $ ^
Match non of the above with:
% egrep '[^]%$^]' file
Note: It is permissible to escape special characters in bracketed expressions when using Perl, or
Perl compatible expressions in PHP.
Testing Regular Expressions
The easiest way to test out a regular expression is using 'egrep' and the standard input.
% egrep '<regular-expression-to-test>'
egrep will read from standard input (no filename supplied). Type a line of text. If the regular
expression matches the line of text will be echoed back, if it doesn't it won't.
Repeating using Regular Expressions
If you wish to apply a wildcard or repeater (see last week's tips) to more that a single
character, enclose the section to match in parenthesis to create a sub-expression and apply the
repeater to that.
Unix_tricks.txt Page 108 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
% egrep 'hello(,hello){1,}'
matches:
hello,hello
hello,hello,hello
hello,hello,hello,hello etc
% egrep '^(Say hello(,hello){1,}, wave goodbye.){1,2}$'
matches:
Say hello,hello, wave goodbye.
or
Say hello,hello,hello, wave goodbye.Say hello,hello,hello, wave goodbye.
Viewing Log Files
This and next week I will cover all things log-file, from viewing them to configuring the logging
daemon.
Most log files live in '/var/log', including the main system log, and log files for netinfo and
the lookup daemon. If you have enabled any of mail, ftp, and printer services, you will see logs
for these too. Most general information, and critical mail information, is written to the system
log.
View log files using 'tail' (system.log in the example), statically with:
% tail -n35 /var/log/system.log
using '-n35' to show the last 35 lines instead of the default 10.
View log files dynamically with:
% tail -n35 -f /var/log/system.log
Press control-c to stop 'tail' and return to the command line.
The command:
% tail -n35 -f /var/log/system.log &
[1] 683 <--------------- note the number you see here.
will constantly interrupt your Terminal screen with the latest log file entry, and ultimately
irritate you :-)
To stop it issue:
% kill nnn
where nnn is the number you noted earlier.
zcat and tlog
Have a look in /var/log. You will notice that log files are 'rolled' periodically: daily for the
system log, and weekly for most others. Rolling involves zipping the most recent log file to
<log-file-name>.0.gz and creating a shiny new (and empty) log file. Previously zipped log files
are rolled - that is renumbered 0->1, 1->2, etc, with the oldest log file being deleted.
View older log files without the hassle of unzipping and re-zipping with the aid of 'zcat'.
% zcat /var/log/system.log.0.gz | tail -n35
Create an alias as a useful short-cut to viewing log files:
Unix_tricks.txt Page 109 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The default umask (permissions for created files) set by the ftp daemon is 027, meaning that
there is no world read access to files you upload. If you wish to change this, for example to
allow Apache access to files you have uploaded, then set the umask:
ftp> site umask 022
This affects only the current ftp session.
A Permanent umask
The umask solution from Tuesday affects only the ftp session from which it was issued. To effect
this permanently, for all logins, you need to configure the ftp daemon.
Edit the file /etc/ftpd.conf (or create it) - you must do so as the root (super) user.
Add a line:
umask all 022
FTP Debugging
The ftp daemon logs to /var/log/ftp.log
If you wish to include extra information in the log file then start the daemon with option -d.
Edit the file:
/etc/xinetd.d/ftp
and add the debug, or any other options you require, to the line
server_args = -l
Security
All Unix account holders may ftp into a server. To prevent a particular user from using ftp add
that user's account name to:
/etc/ftpusers
In order to create a user who cannot log in, but can ftp, set that user's shell to:
/sbin/nologin
and ensure that this shell is listed in:
% cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.
/bin/bash
/bin/csh
/bin/sh
/bin/tcsh
/bin/zsh
/sbin/nologin
Finally, to stop an ftp user from exploring outside of their home directory, 'chroot' them. Add
their account name to:
/etc/ftpchroot
Note that this feature is broken in OS X 10.2.
Unix_tricks.txt Page 114 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Apple's decision to switch ftp daemons from ftpd in 10.1 to lukemftpd in 10.2 is of questionable
merit. Their lack of updated documentation to reflect that decision is truly lamentable. Most
users upgrading from 10.1.x are left hobbled or with malfunctioning ftp servers (especially in
regards to ftpchroot functionality), with no changes in the man pages to help them configure
their new ftp daemon.
One can either fix the broken built-in server by following the instructions here:
http://www.chezludo.com/ or replace it altogether. I elected to replace the built-in ftp server
with pure-ftpd, a robust, efficient and feature-rich ftp server that also boasts no root
exploits.
The first step is to download and unpack the source. First, change directories to wherever you
keep you downloads or source code:
cd /downloads
curl -O ftp://ftp.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.16a.tar.gz
tar xzf pure-ftpd-1.0.16a.tar.gz
cd pure-ftpd-1.0.16a/
./configure --with-everything --with-virtualchroot --without-banner --without-humor --enable-osx
--with-tls
(This will configure a 'big server' with a plethora of options, including throttling, ratios,
ftpwho, quotas, but will leave off the guady initial banner and the sprinkling of colorful banter
in the error messages, etc.)
sudo make install-strip
At this point you will need to choose which server type you desire, as pure-ftpd can run in
either standalone or xinetd mode:
Standalone Mode
You can run the server in standalone mode with this command:
sudo /usr/local/sbin/pure-ftpd &
or if you desire, use command line switches to configure the server at runtime:
sudo /usr/local/sbin/pure-ftpd -A -E -p 40000:50000 -c 5 -C 1 -I 5 -T 25 -u 1 &
The command line switches I have chosen tell the server the following:
-A chroots everyone
-E only allows authenticated users; anonymous users disallowed
-p 40000:50000 specifies the port range for passive connections
-c 5 specifies the number of clients
-C 1 specifies the number of connections per IP address
-I 5 changes the idle timeout; default 15 minutes seems excessive
-T 25 throttles the bandwidth to 25KB/sec per user
Many other switches are available. See the documentation for a complete list.
To get the standalone server to launch automagically at startup, you would have to write a
Startup Item:
http://www.macfora.com/forums/showthread.php?s=&threadid=6314
xinetd Mode
First, get a root shell so all the following commands don't have to be prefaced with sudo:
sudo -s
(As always, before editing a system level file, it is wise to create a backup first.)
cd /etc/xinetd.d/
cp -p ftp ftp.default
pico ftp
Modify the server and server_args lines as folows:
Code:
service ftp
{
Unix_tricks.txt Page 115 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
disable = no
socket_type = stream
wait = no
user = root
server = /usr/local/sbin/pure-ftpd
server_args = -A -E -p 40000:50000 -c 5 -C 1 -I 5 -T 25 -u 1
groups = yes
flags = REUSE
}
Stop xinetd (if running): killall xinetd
Start xinetd to get it to read the new ftp configuration file:
sudo /usr/sbin/xinetd -pidfile /var/run/xinetd.pid
Exit the root shell:
exit
Test to confirm that it is working:
ftp localhost
If you get something like this:
Code:
[gatorparrots:] gator% ftp localhost
Connected to localhost.
220-FTP server ready.
220 This is a private system - No anonymous login
Name (0:gator):
Congratulations! Your new FTP server is working as advertised. To enable the chroot to a single
directory, simply assign your ftp users' home directories to your ftp root directory via NetInfo
(and possibly put them in a dedicated ftp user group for added flexibility). Otherwise, the
individual users will be chrooted to their /Users/username home directory.
Periodic
Periodic system maintenance is scheduled by 'cron' (see Week 33). Cron launches a program called
'periodic' which is responsible for launching the maintenance scripts.
% cat /etc/crontab
...
#minute hour mday month wday who command
# Run daily/weekly/monthly jobs.
15 3 * * * root periodic daily
30 4 * * 6 root periodic weekly
30 5 1 * * root periodic monthly
...
The daily script is run at 3:15 am.
The weekly script is run at 4:30 am Saturday.
The monthly script is run at 5:30 am on the first of each month.
Your Mac must be running (not sleeping) for these scripts to be run.
The output from these scripts can be found in:
% ls /var/log/*.out
/var/log/daily.out /var/log/monthly.out /var/log/weekly.out
Manual Maintenance
Unix_tricks.txt Page 116 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
If your Mac is not switched on in the early hours (see Monday's tip), you will have to run the
maintenance scripts manually. The is done by:
% sudo periodic daily
% sudo periodic weekly
% sudo periodic monthly
Alternatively, edit /etc/crontab and choose times at which your Mac will be running.
Or, use the freeware utility MacJanitor
http://macupdate.com/info.php/id/5856
Roll Your Own
If you wish to add your own maintenance tasks - daily, weekly, or monthly - then create the
following files as necessary.
/etc/daily.local
/etc/weekly.local
/etc/monthly.local
These are regular shell scripts, and once created are run automatically by the default daily,
weekly, and monthly scripts.
Periodic Configuration
The scripts run by periodic for daily, weekly, and monthly maintenance are to be found in:
# ls /etc/periodic/*
/etc/periodic/daily:
100.clean-logs 500.daily
/etc/periodic/monthly:
500.monthly
/etc/periodic/weekly:
500.weekly
Periodic can be called with a full path name to a directory (instead of the words daily, weekly,
or monthly), and it will run all scripts in that directory.
Periodic is configured by the file:
/etc/defaults/periodic.conf
You can add your own setting and place them in the file:
/etc/periodic.conf
See 'man periodic.conf' for more information.
An Example Log Rotator
This script can be placed in /etc/weekly.local. It will rotate log files, much as the standard
weekly script does. The standard script only rotates *.log files is /var/log. This script does
the same for subdirectories such as /var/log/httpd/*.log and /var/log/bind/*.log.
#!/bin/sh -
#########################
# Weekly tidy-up script #
Unix_tricks.txt Page 117 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
#########################
host=$(hostname -s)
echo "Subject: $host - local weekly run output"
# list the subdirectories to be considered, in this case
# /var/log/httpd and /var/log/named
for logtype in httpd named
do
echo ""
echo -n "Rotating type $logtype log files:"
if [ -d /var/log/$logtype ]; then
cd /var/log/$logtype
for log in *.log
do
echo -n " $log"
if [ -f "${log}.3.gz" ]; then mv -f "${log}.3.gz" "${log}.4.gz"; fi
if [ -f "${log}.2.gz" ]; then mv -f "${log}.2.gz" "${log}.3.gz"; fi
if [ -f "${log}.1.gz" ]; then mv -f "${log}.1.gz" "${log}.2.gz"; fi
if [ -f "${log}.0.gz" ]; then mv -f "${log}.0.gz" "${log}.1.gz"; fi
if [ -f "${log}" ]; then
mv -f "${log}" "${log}.0"
/usr/bin/gzip -9 "${log}.0"
fi
touch "$log"
done
case $logtype in
httpd) apachectl graceful;;
named) ndc restart;;
*);;
esac
fi
done
echo ""
echo "Complete"
Filenames With Spaces
Filenames with spaces can cause problems in scripts. (See week 49 for one such example.)
When processing all files in a directory with a 'for' loop, use '*' instead of '`ls`'. For
example:
Unix_tricks.txt Page 118 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
bash$ ls -1
file
file 2
bash$ for file in `ls`; do echo "File: $file"; done
File: file
File: file
File: 2
bash$ for file in *; do echo "File: $file"; done
File: file
File: file 2
Arithmetic
Bash will evaluate an arithmetic expression enclosed in $((...))
bash$ n1=3; n2=4; n3=5
bash$ echo $n1 $n2 $n3
3 4 5
This will not work:
bash$ n="(($n1+$n2)*$n3)"
bash$ echo $n
((3+4)*5)
But this will:
bash-2.05a$ n=$(( ($n1 + $n2) * $n3 ))
bash-2.05a$ echo $n
35
Or use nested $((...)) expansion:
bash$ n=$(( $(( $n1 + $n2 )) * $n3 ))
bash$ echo $n
35
I Do Declare
Bash allows one to 'declare' shell variables. One advantage of declaring variables is that they
can be given specific attributes.
Declare a read-only variable:
bash$ declare -r pi=3.14
bash$ echo $pi
3.14
bash$ pi=20
bash: pi: readonly variable
Variables can also be marked as integers, in which base bash will apply arithmetic evaluation
without the need for $((...)) (see Tuesday):
This will not work:
bash$ declare area1
bash$ area1=5*5
bash$ echo $area1
5*5
But using 'declare -i' this will:
bash$ declare -i area2
Unix_tricks.txt Page 119 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
bash$ area2=5*5
bash$ echo $area2
25
Default Parameter Values
Suppose you wish to set a variable to the value of $1 if $1 is given, otherwise to some default
value.
This can be achieved with:
#!/bin/bash
#set level to $1, or the default 5 if $1 not given
if [ "$1" == "" ]; then
level=5
else
level=$1
fi
Or simpler:
level=${1:-5}
Use Printf
The printf command is very much more powerful than echo. It takes a format string and a set of
arguments to print within the context of that format string.
This statement will print a line, indented by a calculated amount, the label right-aligned, and
the first value in red.
bash$ cat tst
#!/bin/bash
declare -r norm="\033[00m" red="\033[31m"
declare -i indent=${3:-4}
declare -i spacer=20-$indent
printf "%${indent}s%-${spacer}s${red}%7d,${norm}%7d\n" " " "Values:" $1 $2
bash$ tst 1234 5678
Values: 1234, 5678
bash$ tst 1234 5678 8
Values: 1234, 5678
Setting the Prompt
The environment variable PS1 contains the bash shell primary prompt.
Change this with, for example:
$ declare -x PS1="host=\h\$ "
host=saruman$
\h is replaced by the hostname up to the first '.'. \$ is replaced by '$' for normal users, and
Unix_tricks.txt Page 120 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
For example:
$ pwd
/Users/saruman
$ pushd /etc
/etc ~
$ pushd /var/log
/var/log /etc ~
$ pwd
/var/log
$ popd
/etc ~
$ pwd
/etc
$ popd
~
$ pwd
/Users/saruman
$
More History
Be default, a Terminal session saves the last 500 commands in its history. If you are in the
habit of leaving your Terminal open for long periods and would like more history (say 2000
commands) add the following to '.bash_profile' (see Tuesday's tip).
declare -x HISTSIZE=2000;
It makes sense to increase the size of the history file too.
declare -x HISTFILESIZE=2000;
If you have more than one terminal session open, then to prevent the last exited session from
overwriting the history file, set the history merge option in '.bashrc'
shopt -s histappend
This causes histories from different Terminal session to be merged.
Ignore Case
If you wish for bash's tabbed auto filename completion to ignore case, ie:
$ ls ~/doc <hit tab>
will be completed to:
$ ls ~/Documents/
create a file called '.inputrc' in the root of your home directory and add the line:
set completion-ignore-case on
Additionally, to cause auto-completion to list alternatives immediately (bash normally rings the
bell first) add the line:
set show-all-if-ambiguous on
Open Files and Folders
Use the 'open' command to simulate a double click in the Finder.
Here are some examples of its usage.
Unix_tricks.txt Page 122 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Carbon applications are not in the correct binary format for Mac OS X. They must be launched by
'LaunchCFMApp', which does the necessary magic.
On systems prior to Panther, you may notice that using the '-c' option with 'ps' displays
LaunchCFMApp as the application name instead of the application itself. This will cause scripts,
and commands such as 'killall', to fail.
Launch a Carbon Application as root using:
% sudo echo
% sudo -b /System/Library/Frameworks/Carbon.framework/Versions/
Current/Support/LaunchCFMApp /Applications/
Internet\ Explorer.app/Contents/MacOS/Internet\ Explorer &
The 'sudo echo' line gets around password entry problems.
NEVER use 'sudo', or do anything as root, when you've had too many beers.
Default Values
This week's tips apply to both bash script parameters like $1, and shell variables like $my_var.
In the code examples I have used a shell variable.
Set a shell variable to either the value of another variable/parameter, or to a default value if
the other variable/parameter is not set.
$ echo $test
/hello/world
$ echo ${test:-not-set}
/hello/world
I have used 'echo' to illustrate the expansion, but normally one would assign to a variable:
$ var=${test:-not-set}
In the next example the variable 'test2' is not set and so the default value is substituted.
$ echo ${test2:-not-set}
not-set
$ echo $test2
Note that test2 remains unset unless one uses ':=' instead of ':-'
$ echo ${test2:=not-set}
not-set
$ echo $test2
not-set
Error on Unset
Get bash to report an error if a variable or parameter is not set.
$ echo ${test3:?}
bash: test3: parameter null or not set
Write a custom error message with:
$ echo ${test3:?it is not set}
bash: test3: it is not set
An error is reported if a variable does not exist, as above, or is set to null (nothing):
$ test4=
$ echo ${test4:?}
bash: test4: parameter null or not set
Unix_tricks.txt Page 124 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Slicing
Take a slice of a variable.
$ echo $test
/hello/world
$ echo ${test:4}
lo/world
$ echo ${test:4:2}
lo
The first example extracts a slice from the fourth character on (the first being counted as 0),
and the second extracts two characters from the fourth character on.
Determine the number of characters in a variable's value.
$ echo ${#test}
12
Chopping
Chop the head or tail from a variable.
$ echo $test
/hello/world
This example searches for a pattern at the start of the variable ("/hell") and chops it from the
expansion:
$ echo ${test#/hell}
o/world
It is permissible to use the wildcard '*', to match 0 or more occurrences of any characters.
$ echo ${test#*l}
lo/world
In the above example, the shortest string matching '*l' was chopped ('/hel'). In the next, use of
a double # matches the longest string ('/hello/worl').
$ echo ${test##*l}
d
Extract just the script name from the full pathname held in parameter 0. This should be used
within a bash script.
Assuming $0 is /usr/local/bin/script
scriptname=${0##*/}
echo $scriptname
gives:
script
Use % instead of # (and %% instead of ##) to chop from the end of the string.
$ echo ${test%/*}
/hello
$ echo ${test%%/*}
(empty string)
Unix_tricks.txt Page 125 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Replace 'chapter <any text> includes' with 'Section <same text> Contains:' :
:%s/[C|c]hapter \(.*\) [I|i]ncludes/Section \1 Contains:/g
The construct \(...\) captures text in the search pattern, while \1 replays it in the replace
string.
Customising the Search in Vim
Make searches case insensitive with:
:set ignorecase
Now pattern /the matches The, the, THE
Set smartcase so patterns in all lowercase match in a case insensitive manner, but patterns
including uppercase are case sensitive:
:set smartcase
The pattern /the matches The, the, THE
but pattern /The matches only The
Highlight all matches by setting:
:set hlsearch
and switch off with:
:set nohlsearch
and use:
:noh[ighlight]
to temporary turn of the current highlights.
Quick Search in Vim
Find the next occurrence of the word currently under the cursor. In normal mode hit '*', or '#'
to search backwards. Only whole words will be matched, unless you use 'g*' or 'g#'
To search for the next occurrence hit 'n', and for the previous hit 'N'.
Search and Edit in Vim
Use global commands to search for lines to edit. To perform a search and replace, but only on
lines that begin with 'edit', use:
:g/^edit/s/hello/Goodbye/g
The general form of this command is:
g/search-pattern/command
'search-pattern' is a regular expression and 'command' is an 'ex' command. This is the same form
as used by 'sed'.
For more information on the available commands use:
man ex
To delete all lines that start with a '.' use:
:g/^\./d
Unix_tricks.txt Page 127 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The command 'd' deletes the line.
To select all lines that do not match the pattern use 'g!' instead of 'g'
To use normal mode vim commands instead of ex commands use:
g/search-pattern/normal vim-command-list
This example will add '...' to all lines that contain 'edit'
:g/edit/normal A...
The File Command
Determine the type of a file, based on an analysis of its contents, using command 'file':
$ file ~/bin/*
add-user: Bourne-Again shell script text
dhup: Bourne shell script text
crontab: ASCII text
nodif: Tenex C shell script text
test: Mach-O executable ppc
$ file ~/sites/.../*
data: directory
images: directory
include: directory
index.php: C++ program text NOT QUITE!
java-script: directory
jf.html: HTML document text
tc.html: HTML document text
Using the File Command
Use command 'file' in conjunction with other commands to filter file types.
$ find . -print0 | xargs -0 -n1 file
./important-info: directory
./important-info/mac-todo.txt: English text
./important-info/Packing List.rtf: Rich Text Format data, ...
See week 49, Friday in particular.
Filter for all rich text files using grep:
$ find . -print0 | xargs -0 -n1 file | grep -i "rich text"
./important-info/contact-info.rtf: Rich Text Format data, version 1, Apple Macintosh
./important-info/PW.rtf: Rich Text Format data, version 1, Apple Macintosh
./Job/CV.rtf: Rich Text Format data, version 1, Apple Macintosh
Extracting Filenames
Tune Tuesday's example to print only filenames:
$ find . | xargs -n1 file | grep -i "rich text" | awk '{print $1}'
./important-info/contact-info.rtf:
./important-info/PW.rtf:
./Job/CV.rtf:
To remove the trailing ':' tell awk that the field separator is ':':
Unix_tricks.txt Page 128 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ find . | xargs -n1 file | grep -i "rich text" | awk -F: '{print $1}'
./important-info/contact-info.rtf
./important-info/PW.rtf
./Job/Adrian-Mayo-CV.rtf
./Job/job-search.rtf
Finally, edit all the rich text files:
$ open -a textedit $(find . | xargs -n1 file | grep -i "rich text" | awk -F: '{print $1}')
In the tcsh shell use backquotes - `find ...` instead of $(find ...) .
The Hexdump Command
Use command 'hexdump' to display the contents of a binary file, or a file that contains
unprintable characters:
$ hexdump -n32 test
0000000 6109 6201 630a 6e6f 7720 736f 6d65 2074
0000010 6578 7420 616e 6420 7461 6273 2020 0a0d
The file is displayed in hexadecimal, limited to the first 32 bytes (-n32).
One can print in character format, where non-printable characters are either represented in
octal, or escaped (\t = tab)...:
$ hexdump -n32 -c test
0000000 a \t b 001 c \n n o w s o m e t
0000010 e x t a n d t a b s \n \r
...or in hexadecimal plus character, where non-printable characters are represented by a dot:
$ hexdump -n32 -C test
00000000 61 09 62 01 63 0a 6e 6f 77 20 73 6f 6d 65 20 74 |a.b.c.now some t|
00000010 65 78 74 20 61 6e 64 20 74 61 62 73 20 20 0a 0d |ext and tabs ..|
Try this:
$ hexdump -n4 /bin/*
The String Command
Search for ASCII text within binary files using command 'strings'.
a$ strings -a -16 /bin/ls
__dyld_mod_term_funcs
__dyld_make_delayed_module_initializer_calls
The kernel support for the dynamic linker is not present to run this program.
$NetBSD: cmp.c,v 1.14 1998/10/09 02:00:39 enami Exp $
@(#) Copyright (c) 1989, 1993, 1994
The Regents of the University of California. All rights reserved.
$NetBSD: ls.c,v 1.31 1998/08/19 01:44:19 thorpej Exp $
1ACFLRSTWacdfgiklnoqrstuxv
%s: directory causes a cycle
$NetBSD: print.c,v 1.22 1998/07/28 05:15:47 mycroft Exp $
%s %*u %-*s %-*s
$NetBSD: stat_flags.c,v 1.6 1997/07/20 18:53:12 christos Exp $
$NetBSD: util.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $
usage: ls [-1ACFLRSTWacdfgiklnoqrstux] [file ...]
@(#)PROGRAM:ls PROJECT:file_cmds-60 DEVELOPER:root BUILT:Sun Jul 14 03:44:11 PDT 2002
'strings' searches a binary file for a sequence of printable characters, and displays each
sequence. The default minimum sequence length is 4, but can be overridden as in this example
(option -16). Option -a searches all sections of the file (for example, executable files have
Unix_tricks.txt Page 129 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ ls -d *
.DS_Store .dir_colors .mplayer Desktop
.FBCIndex .emacs.d .my.cnf Development
.gimp-1.2 .mysql_history Documents .gvimrc
...
(5) Enable enhanced pathname expansion. Normally just '*', '?', and '[...]' are recognized (as in
'ls *' or 'ls file.???').
$ shopt -s extglob
The following extended globbing is enabled:
?(pattern-list) Match 0 or 1 occurrence of the given patterns
*(pattern-list) Match 0 or more occurrences of the given patterns
+(pattern-list) Match 1 or more occurrences of the given patterns
@(pattern-list) Match exactly one of the given patterns
!(pattern-list) Match anything but one of the given patterns
Prompt Commands
Have bash execute a command before it issues a prompt.
To print the date before each prompt:
$ declare -x PROMPT_COMMAND='date'
Mon Feb 2 12:02:41 GMT 2004
$
Write a message to the title bar of the Terminal window (in this example the output from command
date):
$ declare -x PROMPT_COMMAND='echo -n "^[]0; $(date)^G"'
Notes:
^[ = escape, got by typing contol-V then escape.
^G = control G, got by typing control-V control-G.
This works for xterm and Apple's Terminal.app
This can be useful:
$ declare -x PROMPT_COMMAND='echo -n "^[]0; $TITLE^G"'
$ declare -x TITLE="Hello"
Now any executing command or script can report to the title bar by setting environment variable
TITLE.
Aliases With Parameters
Unlike tcsh, bash aliases do not accept parameters. This is because bash provides a more powerful
mechanism by way of functions.
Write a function called 'word' which searches the Webster's dictionary at /usr/share/dict.web2:
$ function word () { grep $* /usr/share/dict/web2; }
$ word apple
apple
appleberry
appleblossom
applecart
appledrane
applegrower
Unix_tricks.txt Page 131 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
applejack
...
$* expands to all parameters passed to function 'word'. $1 expands to parameter 1, etc.
To list all functions use:
$ declare -f
Extending the 'alias' Command
If you wish for the 'alias' command to list functions (see Thursday's tip) declare this function:
function alias ()
{
if [ -z $* ] ; then
builtin alias;
declare -f;
else
builtin alias $* ;
fi
}
Basic HereDoc
Redirect text to standard input using Here Documents.
$ cat hd
#!/bin/bash
echo "Test"
cat <<EOD
Hello there.
This text is the result
of a 'Here Document'
EOD
echo "End"
The section between '<<EOD' and the line containing just 'EOD' is read by the shell and becomes
standard input for the preceding command. In this case 'cat', given no filename parameters, will
read standard input (the supplied text) and write it to standard output.
$ ./hd
Test
Hello there.
This text is the result
of a 'Here Document'
End
$
Variable Expansion
Control variable expansion within a HereDoc.
Shell variables within a HereDoc are expanded as usual:
$ cat hd
#!/bin/bash
Unix_tricks.txt Page 132 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
today=$(date)
cat <<EOD
Hello there. Today is $today.
EOD
$ ./hd
Hello there. Today is Mon Feb 16 11:53:46 GMT 2004.
$
But, if the delimiter is quoted no variable expansion is performed:
$ cat hd
#!/bin/bash
today=$(date)
cat <<'EOD'
Hello there. Today is $today.
EOD
$ ./hd
Hello there. Today is $today.
$
Remove Indentation
Remove shell script indentation from HereDoc text.
Non-trivial shell scripts usually nest statements, and it is natural to use indentation. To allow
the HereDoc to follow the natural flow of indentation, without including the tab characters in
the HereDoc text, use '<<-'.
$ cat hd
#!/bin/bash
if [ "$1" = "" ]; then
cat <<-EOD
Hello there.
Today is $(date).
EOD
fi
$ ./hd
Hello there.
Today is Mon Feb 16 12:08:16 GMT 2004.
$
Read Data
Use a HereDoc and a function to set variables.
$ cat hd
#!/bin/bash
read_data ()
Unix_tricks.txt Page 133 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
{
read make
read model
read colour
}
read_data <<HEREDOC
BMW
3 series
Blue
HEREDOC
echo "Make: $make, model: $model, colour: $colour"
$ ./hd
Make: BMW, model: 3 series, colour: Blue
$
Comment Out / Test Variables
Thanks to Ian Ferguson for Friday's Tips.
Use HereDocs to comment out code, avoiding having to add '#' to the start of every line:
$ cat hd
#!/bin/bash
echo "before"
:<<'EOD'
if [ "$1" = "" ]; then
echo "No parameter given"
fi
EOD
echo "after"
$ ./hd
before
after
$
Here the 'if' statement has been commented out.
Use HereDocs to display an error message if a variable is not set:
$ cat hd
#!/bin/bash
var1=hello
: <<ERRORIFNOTSET
${var1?} ${var2?}
ERRORIFNOTSET
$ ./hd
./hd: var2: parameter null or not set
$
Unix_tricks.txt Page 134 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Process on Extension
Rename a group of files in a single command.
This will *not* work:
$ mv *.html *.php
usage: mv [-fi] source target
mv [-fi] source ... directory
The following script, called 'ext' for extension-based, will let you form such a command:
$ cat ext
#!/bin/bash
shopt -s nullglob
for file in *.$2; do
$1 "$file" "${file%.$2}.$3"
done
Use 'ext' like this to rename (mv) *.html to have an extension of .php:
$ ext mv html php
Use any similar command, such as 'cp', with the 'ext' script.
Add parameter checking to the script:
$ cat ext
#!/bin/bash
if [ "$3" = "" ] || [ "$4" != "" ]; then
echo 'For each file *.ext1, executes "command file.ext1 file.ext2"'
echo ' eg "cp html php" to copy all html files to php equivalents'
echo ' "mv html php" to rename all html files to have a php extension'
echo "Usage: ${0##*/} command ext1 ext2"
exit
fi
# this option ensures that no match for *.$2 returns null not *.$2
shopt -s nullglob
for file in *.$2; do
$1 "$file" "${file%.$2}.$3"
done
Recursive Process on Extension
Rename a group of files recursively in a single command. The script 'rext' does the same as
Monday's 'ext' script, but applies the command to all matching files in the directory hierarchy.
It uses the 'ext' script.
$ cat rext
#!/bin/bash
for dir in $(find . -type d); do
pushd $dir > /dev/null
ext $1 $2 $3
popd > /dev/null
done
Unix_tricks.txt Page 135 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Use 'rext' like this to copy (cp) *.php to equivalent files with an extension of .html:
$ rext cp php html
Use any similar command, such as 'mv', with the 'rext' script.
Add parameter checking to the script:
$ cat rext
#!/bin/bash
if [ "$3" = "" ] || [ "$4" != "" ]; then
echo 'Executes "command file.ext1 file.ext2" on each matching file file in'
echo ' the directory *hierarchy*'
echo ' eg "cp html php" to copy all html files to php equivalents'
echo ' "mv html php" to rename all html files to have a php extension'
echo "Usage: ${0##*/} command ext1 ext2"
exit
fi
for dir in $(find . -type d); do
pushd $dir > /dev/null
ext $1 $2 $3
popd > /dev/null
done
Process Multiple Files
If you have a script or command that is not able to take a list of files, use the 'each' script
to simulate this. 'each' executes the given command once for each file.
$ cat each
#!/bin/bash
filetype=$1
shift
for file in $filetype; do
$* "$file"
done
Use the script like this to apply 'my-script' to all .html files in the current directory:
$ each \*.html my-script -options ...
Add parameter checking to the script:
$ cat each
#!/bin/bash
if [ "$2" = "" ]; then
echo "Executes a command on each matching file in the current directory"
echo "Usage: ${0##*/} filetype command-to-execcute"
exit
fi
Unix_tricks.txt Page 136 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
filetype=$1
shift
for file in $filetype; do
$* "$file"
done
Recursive Process Multiple Files
The script 'reach' does the same as Wednesday's 'each' script, but executes the given command on
all matching files in the directory hierarchy.
$cat reach
#!/bin/bash
filetype=$1
shift
find . -name "$filetype" -print0 | xargs -0 -n1 $*
Use the script like this to apply 'my-script' to all .php files in the directory hierarchy:
$ reach \*.php my-script -options ...
Add parameter checking to the script:
% cat reach
#!/bin/bash
if [ "$2" = "" ]; then
echo "Executes a command on each matching file in the directory *hierarchy*"
echo "Usage: ${0##*/} filetype command-to-execute"
exit
fi
filetype=$1
shift
find . -name "$filetype" -print0 | xargs -0 -n1 $*
Sed Extension
Create a simple wrapper for the 'sed' command (or any similar command) to enable it to process a
file directly. Normally sed uses standard in and out, and cannot write back to its input file.
$ cat sedx
#!/bin/sh
tmp=tmp-file-for-$PPID
{ sed "$1" "$2" > $tmp \
&& mv $tmp "$2"
} || rm $tmp
Note that the more obvious line:
sed "$1" "$2" > $tmp ; mv $tmp "$2"
should not be used as an error in the sed script will cause all files to be wiped.
Unix_tricks.txt Page 137 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Use sedx in this way:
$ sedx 's/XXX/ZZZ/' index.html
to apply the sed command to index.html.
The script as given takes only one input file. Use the scripts from Wednesday and Thursday to
process multiple files.
Apply to all files in the current directory:
$ each \*.html sedx 's/XXX/YYY/'
Or to all files in the directory hierarchy (recursively):
$ reach \*.html sedx 's/XXX/YYY/'
Add parameter checking to the script:
$ cat sedx
#!/bin/sh
if [ "$2" = "" -o "$1" = "-usage" ]; then
echo "Executes a sed command writing back to the original file."
echo "Usage: ${0##*/} sed-command file"
echo "Or use with (r)each"
echo "Usage: (r)each filetype ${0##*/} sed-command"
exit
fi
tmp=tmp-file-for-$PPID
{ sed "$1" "$2" > $tmp \
&& mv $tmp "$2"
} || rm $tmp
Split Screen
This week's tip refer to Apple's Terminal application (/Applications/Utilities/Terminal.app).
Select split screen mode in the Terminal by clicking the icon in the top right corner...
One can scroll the top frame to view previous commands while working as normal in the bottom
frame. This allows copy and paste between the two frames.
Copy and Paste
Copy text in terminal window by highlighting it as you would in any other application (left click
and drag). The selected text is automatically put into a buffer which can be recalled by clicking
the middle mouse button*
To paste between different windows or applications, use the usual command-C (after highlighting
text) to copy, and command-V to paste. The Terminal also supports drag and drop on the selected
text.
Two command-line utilities are provided to copy and paste:
$ pbpaste
Two command-line utilities are provided to copy and paste:
Unix_tricks.txt Page 138 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ machine
ppc7450
$ uname -v
Darwin Kernel Version 7.3.0: Fri Mar 5 14:22:55 PST 2004;
root:xnu/xnu-517.3.15.obj~4/RELEASE_PPC
$ hostinfo
Mach kernel version:
Darwin Kernel Version 7.3.0:
Fri Mar 5 14:22:55 PST 2004; root:xnu/xnu-517.3.15.obj~4/RELEASE_PPC
Kernel configured for a single processor only.
1 processor is physically available.
Processor type: ppc7450 (PowerPC 7450)
Processor active: 0
Primary memory available: 768.00 megabytes.
Default processor set: 94 tasks, 249 threads, 1 processors
Load average: 2.37, Mach factor: 0.22
$ system_profiler
(much output)
Working with (B)Zipped Files
If you wish to work with zipped files, use the z* set of commands. For example, use 'zmore' to
view a file instead of unzipping, viewing, the possibly rezipping.
$ ls /usr/bin/z*
/usr/bin/zcat /usr/bin/zdiff /usr/bin/zgrep /usr/bin/zmore /usr/bin/zprint
/usr/bin/zcmp /usr/bin/zforce /usr/bin/zip /usr/bin/znew
If the files are compressed with bzip2, use the bz* set of commands:
$ ls /usr/bin/bz*
/usr/bin/bzcat /usr/bin/bzegrep /usr/bin/bzip2 /usr/bin/bzmore
/usr/bin/bzcmp /usr/bin/bzfgrep /usr/bin/bzip2recover
/usr/bin/bzdiff /usr/bin/bzgrep /usr/bin/bzless
Talking Scripts
Use the 'say' command directly (i.e. without going through osascript) to add speech to scripts:
$ say "Hello world."
Lookup some words to say with look:
$ look zy
zyga
zygadenine
Zygadenus
Zygaena
Unix_tricks.txt Page 140 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
zygaenid
Zygaenidae
...
Or search the dictionary with grep:
$ grep "^d...p$" /usr/share/dict/web2
dadap
decap
dreep
droop
Or the online dictionary for the meaning of 'fuci':
$ curl dict://dict.org/d:fuci
220 pan.alephnull.com dictd 1.8.0/rf on Linux 2.4.18-14 <auth.mime>
<1904283.29426.1084790981@pan.alephnull.com>
250 ok
150 1 definitions retrieved
151 "Fuci" web1913 "Webster's Revised Unabridged Dictionary (1913)"
Fucus \Fu"cus\, n.; pl. {Fuci}. [L. rock lichen, orchil, used as
a red dye, red or purple color, disguise, deceit.]
1. A paint; a dye; also, false show. [Obs.]
2. (Bot.) A genus of tough, leathery seaweeds, usually of a
dull brownish green color; rockweed.
Note: Formerly most marine alg? were called fuci.
.
250 ok [d/m/c = 1/0/22; 0.000r 0.000u 0.000s]
221 bye [d/m/c = 0/0/0; 0.000r 0.000u 0.000s]
Software Update
Perform a software update, perhaps remotely, with the Software Update command line tool:
$ softwareupdate -l
Software Update Tool
Copyright 2002-2003 Apple Computer, Inc.
Your software is up to date.
Don't forget 'nvram' to manipluate open firmware parameters:
To list nvram variables:
$ nvram -p
To set verbose login (equivalent to Command-V on startup)
$ sudo nvram boot args="-v"
Use 'bless' to change the boot volume:
sudo bless -folder '/Volumes/vol-name/System/Library/CoreServices' -setOF
sudo bless -folder '/System/Library/CoreServices' -setOF
Image Manipulation
Use the Scriptable Image Processing System (sips command) to manipulate images via the command
line or AppleScript.
Unix_tricks.txt Page 141 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ ls -al osxfaq
then:
$ cd escape-.
will be changed into
$ cd osxfaq
- particularly useful for long or awkward filenames.
Additionally, the shell variable $_ can be used:
$ echo $_
osxfaq
Command Line Expansion
You probably know that hitting tab will complete a filename. Use Tab to also complete commands,
aliases, and functions.
If the word begins with $, then tab completion will complete based on the currently defined shell
variables. Similarly, ~ (tilde) completes based on user names, and @ completes based on
hostnames.
For @ to work, the variable HOSTFILE must contain the name of a file, which contains entries in
the same format at /etc/hosts. (See 'man hosts'.)
Avoiding History
Prevent particular lines from entering the command history; useful if the command contains
sensitive information such as a password.
There are two techniques:
Set the environment variable HISTCONTROL:
$ declare -x HISTCONTROL="ignoreboth"
Now command lines starting with space are not entered into the history. Additionally, lines
matching the last line in the history are not added.
To control these aspects individually, use 'ignorespace' or 'ignoredups' instead of 'ignoreboth'.
The second technique is to set the environment variable HISTIGNORE to a colon-separated list of
patterns. For full details see 'man bash' and search for HISTIGNORE.
Getting the Right Help
Use Bash's help system for information on its built-in commands. (These will not be found in the
Unix man pages.)
$ help
to list them all.
$ help <command>
to display the help page for the given command.
Supply a pattern and Bash will display a help page for each command that matches the pattern, or
a short synopsis if option -s is given.
Unix_tricks.txt Page 143 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ help -s "re*"
Getting the Right Command
When you issue a command such as 'ls', Bash may execute an alias, a function, a built-in, or one
from the search path ($PATH). Determine the type of command Bash will execute using the command
'type'. This in analogous to the 'which' command in the tcsh shell.
$ type ls
ls is aliased to `/usr/local/bin/ls --color=tty'
View all possibilities with:
$ type -a ls
ls is aliased to `/usr/local/bin/ls --color=tty'
ls is /usr/local/bin/ls
ls is /bin/ls
Forcing the Right Command
Tuesday's tip shows you how to determine which variant of a command Bash will execute. What if
you wish to override this?
Bash's search order is:
aliases, keywords, functions, built-ins, commands from $PATH
Use the following Bash built-ins to change the search order for a one-off command:
* 'command' forces only built-ins or commands from $PATH to be considered, ignoring aliases,
keywords, and functions
* 'builtin' forces only built-ins to be considered
* 'enable' forces only commands from $PATH to be considered
For example:
$ command ls
forces ls from $PATH to be executed, overriding any alias or function you may have defined for
ls.
Note: the 'tcsh' shell uses a backslash
% \ls
to override aliases and built-ins.
Aliases
List all aliases with:
$ alias
Set an alias with:
$ alias la="/bin/ls -Al"
Remove an alias with:
$ unalias
Here is a trick to alias directory names. Normally Bash does alias substitution on the first word
of a command line (the command) only...
$ pwd
/Users/saruman
Unix_tricks.txt Page 144 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ alias pref="~/Library/Preferences/"
$ cd pref
-bash: cd: pref: No such file or directory
...hence this does not work.
But alias cd as below (note the trailing space)...
$ alias cd="cd "
$ cd pref
$ pwd
/Users/saruman/Library/Preferences
... and now it works.
Functions
In Bash, unlike the tcsh shell, aliases cannot take parameters. Bash has the more powerful
'function' construct.
Declare a function 'psx':
$ psx () { ps axww | grep -i $* | grep -v grep;}
The special variable $* is expanded to all parameters passed to the function. Alternatively, use
$1, $2, ... for the first and second (and so on) parameters. This is exactly the same scheme as
that used to expand parameters passed to shell scripts.
$ psx safari
503 ?? S 12:36.51 /Applications/Safari.app/Contents/MacOS/Safari -psn_0_2359297
Here is a function to view log files:
$ tlog () { tail -f -n40 /var/log/$1.log;}
And:
$ tlog system
will view the system log (control-C to exit).
To list all functions:
$ declare -f
or
$ declare -F
to list just the function names.
To Sub-Shell or Not to Sub-Shell
Know the difference between running a shell and sourcing it. This example illustrates.
$ declare -x NEW_ENV_VAR="XXXX"
$ new_var="xxxx"
$ echo $NEW_ENV_VAR
XXXX
$ echo $new_var
xxxx
$ cat tst
Unix_tricks.txt Page 145 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
#!/bin/bash
declare -x NEW_ENV_VAR="TEST"
new_var="test"
Run the script:
$ ./tst
$ echo $NEW_ENV_VAR
XXXX
$ echo $new_var
xxxx
Notice the variables haven't changed...
Source the script:
$ source tst
$ echo $NEW_ENV_VAR
TEST
$ echo $new_var
test
...and this time they have.
Running a script as in the first example launches a new shell. When the new shell exits its
environment is not passed back to the parent shell, and hence its environment/shell variables are
lost, and those of the original shell remain unchanged.
'Sourcing' a script executes it with the current shell. The environment/shell variables set by
the script are those of the current shell and so remain set after the script has completed.
Note that:
$ source ./tst
and
$ . ./tst
are equivalent.
Parenthesis
Use parenthesis to execute commands in a sub-shell.
$ cat tst2
new_var="xxxx"
echo $new_var
(new_var="test")
echo $new_var
$ ./tst2
xxxx
xxxx
You might have expected this script to echo
xxxx
test
Because the second assignment is in parenthesis it is executed as a sub-shell, and (remembering
Monday's tip) has no effect on the parents shell's environment.
You may wish to execute many commands in a sub-shell when the output from all of them is
Unix_tricks.txt Page 146 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
redirected or piped.
As a illustrative example:
$ echo hello; echo world > outfile
hello
$ cat outfile
world
$ (echo hello; echo world) > outfile
$ cat outfile
hello
world
Braces
Use braces to execute a sub-shell.
Tuesday's tip executed a list of commands in parenthesis:
$ (echo hello; echo world) > outfile
You can achieve the same effect with braces:
$ { echo hello; echo world;} > outfile
except that the command list is executed as part of the current shell, not as a sub shell. (Like
the difference between running and sourcing as explained in Monday's tip.)
Note the space after the opening brace and the semicolon before the closing brace. The syntax
requirements are stricter for braces than for parenthesis.
eval
Use 'eval' to override the shell's command line parsing order.
Eval is a subtle but useful command. Consider this script to run commands in the background.
$ cat tst
#!/bin/bash
"$*" > ~/outfile &
$ tst 'du -sk *'
./tst: line 2: du -sk *: command not found
$ tst 'ls | grep "^D"'
./tst: line 2: ls | grep "^D": command not found
The script fails because the shell takes the first token ("$*") as the command and *then* expands
$*. We need to change the shell's order of evaluation. Removing the quotes around "$*" won't
quite work:
$ cat tst
#!/bin/bash
$* > ~/outfile &
This is fine:
$ tst 'du -sk *'
$ cat ~/outfile
5524 Desktop
1992 Development
...
But the second command still fails. Again, the shell tokenises before it expands, and hence fails
Unix_tricks.txt Page 147 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
daemon /usr/bin/false
...
saruman /bin/bash
nireport takes a directory (/users in this example) and a list of properties to display.
List NetInfo directories with nidump. nidump displays NetInfo directories in the standard Unix
flat file format:
$ nidump group .
nobody:*:-2:
nogroup:*:-1:
wheel:*:0:root
...
saruman:*:501:
Load information into NetInfo with niload. niload will load the database from standard Unix flat
files:
$ sudo niload -dm group . < flat-file
Check 'man niload' to see if you require the 'd'elete or 'm'erge options.
Manage the database with niutil or nicl. I prefer to use 'dscl' for this.
Backup and Restore
The NetInfo database is backed up daily into:
/private/var/backups/local.nidump
You can do this yourself with:
$ nidump -r / -t localhost/local > local.nidump
Restore a broken database from backup:
Boot into single user mode - hold down command-s on startup and wait for the text to finish
scrolling. Issue the commands:
$ mount -uw /
$ mv /var/db/netinfo/local.nidb /var/db/netinfo/corrupt.nidb
The next steps were taken from an article by O'Reilly. They start up NetInfo and create an empty
database.
$ /usr/libexec/kextd
$ /usr/sbin/configd
$ /sbin/SystemStarter
$ /usr/libexec/create_nidb
$ /usr/sbin/netinfod -s local
Finally use niload to load the backed up database:
$ niload -d -r / . < /path/to/backed-up/database
Override Lookup Order
In Panther, most of the Unix flat files are searched, as well as NetInfo and Directory Services.
Check this search order for services with:
$ lookupd -configuration
Change this for any service by creating a file in directory '/etc/lookupd/'. For example, to
control how NFS mounts are searched for (by default NetInfo but not the /etc/fstab 'flat file')
Unix_tricks.txt Page 150 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Run trap1:
$ ./trap1
Hello........
^XOn no you dont
Hello........
not that way either
Hello........
Hello........
In another window (using the killsig function from Monday):
$ killsig INT trap1
$ killsig TERM trap1
$ killsig KILL trap1
Syntax:
trap "command to execute" SIGNAL-LIST
Trap Functions
Use a function if you need a more complex trap handler:
$ cat trap2
#!/bin/bash
handlehup ()
{
echo "Got a HUP"
echo "Will now reload my config"
#......
}
trap "handlehup" HUP
while true; do
echo "Hello........"
# use sleep to represent a lengthy block of code
sleep 1
done
If this script is sent the HUP signal, it will be caught by 'trap' and function 'handlehup' will
be executed. When the function completes the scrip will continue to execute from where it left
off.
$ ./trap2
Hello........
Got a HUP
Will now reload my config
Hello........
^C
$
In another window (using the killsig function from Monday):
$ killsig HUP trap2
The HUP signal (hangup) is traditionally used to tell a process to reload its configuration and
restart. Apache will respond to a HUP signal in this way (although one would normally use
'apachectl').
Unix_tricks.txt Page 152 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Trap Over a Section of Code
Limit your trap functions to just the critical part of a script, often around a number of
commands that should not be interrupted mid-way.
This script traps HUP, INT and TERM signals around the critical code, then drops the handler
around the normal code.
$ cat trap3
#!/bin/bash
handlesig ()
{
echo "Got an INT"
#......
}
# critical code - stop interrupts
trap "handlesig" HUP INT TERM
echo "Critical code"
# use sleep to represent a lengthy block of code
sleep 1; sleep 1; sleep 1; sleep 1; sleep 1; sleep 1
sleep 1; sleep 1; sleep 1; sleep 1; sleep 1; sleep 1
# normal code - allow interrupts
trap - HUP INT TERM
echo "Normal code"
sleep 1000
The statement:
trap - SIGNAL-LIST
removes a handler from the listed signals and replaces it with the default (if any).
$ ./trap3
Critical code
Got an INT
^CGot an INT
Normal code
Hangup
In another window (using the killsig function from Monday):
$ killsig HUP trap3
(wait a bit until normal code is entered)
$ killsig HUP trap3
Trap Over a Section with Sub-Shells
Limit your trap functions to just the critical part of a script, as Thursday, but use sub-shells.
Unix_tricks.txt Page 153 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Use:
$ jobs -l
[1]+ 707 Running xterm &
to display a list of jobs and their process numbers and PIDs.
Note that each instance of bash (each terminal window) launches and controls its own list of
jobs. Typing 'jobs -l' on another terminal will not show the job with PID 707.
Jobs That Require IO
Generally, scripts that require input/output are not run in the background. Output will be
interspersed with output from the current foreground job, and input won't work.
Use file redirection if a background job produces output, or requires predictable input:
$ cat script
#!/bin/bash
read -p "Name: " name
echo You claim to be $name.
...
$ cat in
Adrian
$ ./script <in &>out &
[7] 809
$
When the job completes the terminal will display:
[7] Done ./script <in >&out
And to prove it worked:
$ cat out
You claim to be Adrian.
Background a Running Job
Re-gain terminal control from a job you should have backgrounded on launch. Hit control-z to
suspend the job, then type 'bg' to place it in the background. The job will then continue running
as if you had launched it in the background using '&'.
$ xterm (oops! I forgot the &)
^Z
[7]+ Stopped xterm
$ bg
[7]+ xterm &
$ (control returned to the current terminal)
Swapping Jobs
Use 'fg' and 'bg' to manage background jobs.
$ fg %n
brings job number n into the foreground. 'fg' with no parameters operates on the most recently
Unix_tricks.txt Page 155 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
backgrounded job. You can also specify the PID number to 'fg' by omitting the %.
Supply terminal input to a background job (this example uses the script from Tuesday's tip):
$ ./script &
[2] 905
Name: $
[2]+ Stopped ./script
Here, the Terminal indicates the job 2 has stopped. Use 'jobs -l' to find out why...
$ jobs -l
[1]- 893 Running xterm &
[2]+ 905 Stopped (tty input) ./script
...it requires input the the terminal '(tty input').
Bring the job into the foreground:
$ fg %2
./script
Now supply the input:
Adrian
You claim to be Adrian.
$ bg
will send the job back into the background if it needs to run more. 'bg' can take %job-number or
PID as for 'fg'. Without any parameters it assumes the most recently foregrounded job.
Waiting...
If a shell (or script) exists while background jobs that it controls are still running, those
jobs become orphaned. They can only be stopped with:
$ kill -KILL <PID>
Prevent a script from creating orphaned jobs using 'wait'.
$ wait
will prevent the shell (and therefore the script) from exiting until all background jobs under
its control have completed.
$ wait %n
waits only for job number n. You may specify a PID too, without the preceding '%'.
[ vs Test
Use:
$ man test
or:
$ man [
to learn about the conditions one can use in Bash and Bourne shell scripts for 'if', 'while', and
'until' statements.
Unix_tricks.txt Page 156 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Conditions in Bash are evaluated by the command 'test', which can test files, strings, and
numbers. The command is also called '[' which explains the form of a Bash condition.
$ if [ 3 -lt 7 ]; then
echo "3 is less than 7"
else
echo 'very odd!'
fi
3 is less than 7
An equivalent form is:
$ if test 3 -lt 7; then ...
Why is ';' required before 'then'?
Conditions and Statements
Make more use of Bash's conditions - understand that a condition is actually a command. Bash will
execute the 'condition' and test the return value.
0 (success) = true
not 0 (failure) = false
This ties in with Monday's explanation of the 'test' and '[' commands.
For example:
$ if diff f1.txt f2.txt; then
echo "Files are the same"
else
echo "Files are different"
fi
Files are the same
The return value of a command is held in shell variable $?
$ diff f1.txt f2.txt
$ echo $?
0
AND and AND and OR or OR
Use AND and OR constructs for more complex conditions.
$ a=1;b=2;c=3
$ if [ $a -lt $b -a $b -lt $c ]; then
echo "a < b < c"
else
echo "not so"
fi
a < b < c
The test statement uses operators -a for AND and -o for OR.
Alternatively, use Bash's AND and OR operators:
$ if [ $a -lt $b ] && [ $b -lt $c ]; then
echo "a < b < c"
else
echo "not so"
Unix_tricks.txt Page 157 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
fi
a < b < c
The difference between the two:
In the first example the 'test' command '[ ... ]' evaluated the complete expression.
In the second example 'test' was called twice, and bash evaluated 'first test result' AND 'second
test result'.
Bash's Built-in Conditions
Use Bash's built-in tests for integer arithmetic (and only integer arithmetic). The use of '<'
instead of '-lt' makes tests more readable. The expression is enclosed in (( ... )) rather than [
... ].
$ if (( ($a < $b) && ($b < $c) )); then
echo "a < b < c"
fi
a < b < c
Alternatively:
$ if (($a < $b)) && (($b < $c)); then
echo "a < b < c"
fi
a < b < c
Understand the difference between the two forms.
More Complex Tests
Understand how to form complex expressions. Sometimes expressions need to be bracketed to force
the correct order of evaluation. (Though in the simple example below bracketing is not strictly
necessary.)
$ a=3;b=2;c=1
$ if [ ( $a -lt $b ) -a ( $b -lt $c ) ]; then
echo "a < b < c "
else
echo "not so "
fi
-bash: syntax error near unexpected token `$a'
Syntax error, we must escape the bracketed expression.
Try:
$ if [ '( $a -lt $b ) -a ( $b -lt $c )' ]; then
echo "a < b < c "
else
echo "not so "
fi
a < b < c
Unix_tricks.txt Page 158 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Any more...?
Quoting and Escaping
Prevent bash from interpreting special characters (see Monday's tip).
There are three methods:
1) Backslash escaping
$ a=5;b=4;c=3
$ echo \$1 \* \$b > \$c
$1 * $b > $c
None of $, *, or > were interpreted specially.
2) Single quotes (strong quoting)
$ echo '$a * $b > $c'
$a * $b > $c
Everything with in '...' is treated literally: the shell does not interpret any special
characters.
3) Double Quotes (weak quoting)
$ echo "$a * $b > $c"
5 * 4 > 3
Everything but $ and \ within "..." is treated literally: this allows you to escape special
characters while still expanding variables.
If you wish to mix literal $ and variable expansion $ use either backslash to escape the $:
$ echo "\$1 * $b > \$$c"
$1 * 4 > $3
Quoting Tricks
1) Quoting Parameters
Sometimes it is necessary to use single quotes around parameters, as in the awk example below:
$ echo "Adrian's" | awk '{print "You cant have " $1 " $1"}'
You cant have Adrian's $1
If I wanted to output "can't" instead of "cant", a first attempt might be:
$ echo "Adrian's" | awk '{print "You can\'t have " $1 " $1"}'
> (hit control-c)
It is not possible to escape the ' in can't because it lies within a '...'ed string.
Use:
$ echo "Adrian's" | awk '{print "You can'\''t have " $1 " $1"}'
You can't have Adrian's $1
This splits the awk parameter into three adjacent strings:
'{print "You can'
\'
't have " $1 " $1"}'
Unix_tricks.txt Page 160 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
2) Remember that single quotes are not special within double quotes, so you can also use:
$ echo "You can't have my \$"
You can't have my $
or even:
$ echo "You can't have my $"
You can't have my $
because $ with nothing following is take as a literal $
Shell Evaluation
Understand the shell's evaluation order.
This does not work:
$ file="~/Desktop/"
$ ls $file
/usr/local/bin/ls: ~/Desktop/: No such file or directory
$ ls ~/Desktop/
A File
Tilde (~) is a shell special character, and has no meaning to the file system. Placing it in
quotes prevents the shell from expanding it. You might think it is expanded in the expression:
ls $file
but this is not so. The shell expands '~' before it expands $variables, and therefore never sees
the '~' until too late.
Ways around this:
1) Use eval
$ eval ls $file
A File
eval gives the shell a second go at expansion. The first pass $file is expanded, and then tilde
is expanded by 'eval'.
(See Week 78, Thursday for examples of 'eval'.)
2) Don't quote
$ file=~/Desktop
$ echo $file
/Users/saruman/Desktop
$ ls $file
A File
The first line is not quoted, so file contains the full pathname instead of '~/Desktop'
If you need to quote the assignment to variable file, use $HOME.
$ file="$HOME/Desktop"
$ ls $file
A File
$HOME is expanded on the assignment whereas ~ is not.
Unix_tricks.txt Page 161 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Multi-level Quoting
Count the number of times the shell will evaluate an expression.
This is a simple example which illustrates the principle.
$ ls A\ Dir
A File
$ echo ls A\ Dir > script
$ ./script
ls: A: No such file or directory
ls: Dir: No such file or directory
The script does not work because the shell processed the backslash escaped space when executing
echo. The file does not therefore contain the escaping:
$ cat script
ls A Dir
One must escape the escaping by writing escape-space to the file. Both must be escaped themselves
- we must write escape-escape escape-space:
$ echo ls A\\\ Dir > script
$ ./script
A File
Mount AFP Shares
Mount an AFP share using the Terminal.
An AFP share can be mounted anywhere on the filesystem - for example in /Volumes or at the root
of your home directory. The 'mount point' must be an existing directory.
For example, to mount my home directory from another machine (melkor) onto the mount point
'melkor' in my home directory on this machine, I use:
$ cd ~
$ mkdir melkor
$ mount -t afp afp://myuser:mypassword@melkor.mayo-family.com/myuser melkor
mount_afp: the mount flags are 0000 the altflags are 0020
'@melkor.mayo-family.com/myuser' is the hostname or IP address of the server followed by the AFP
share point as seen in the Finder's 'Connect...' dialogue.
$ ls
Desktop Documents Movies Pictures Sites melkor
Development Library Music Public bin osxfaq
To unmount use:
$ umount melkor
and the directory will be deleted.
AFP Mounting Script
This script is useful if you have accounts on several machines (desktop and laptop). It mounts
your home directory from the other machine.
The simplest usage is:
Unix_tricks.txt Page 162 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ mount-user
$ mount-user -usage
mount youruser's home directory from the server yourserver
Usage: mount-user [-p password] [-h host] [-u user]
[-p password] - use an AFP password if required
[-h host] - override yourserver as the server to mount
[-u user] - override youruser as the user to mount
It assumes the following.
Your user name is the same on both machines, if not override with option '-u otherusername'.
The server name is defined in the environment variable $ALM_SERVER, if not override with option
'-h hostname/ip-address'.
The mountpoint will be $ALM_AFP_MOUNT/hostname/username, where $ALM_AFP_MOUNT is an environment
variable. The pathname in $ALM_AFP_MOUNT must exist. /Volumes is the usual pathname, but you may
want to change it to avoid confusion with Finder-mounted shares (eg to /nfs).
The script will create all the directories as necessary.
#!/bin/bash
# mounts the current user's home directory on the server
# $ALM_SERVER into the mount $ALM_AFP_MOUNT/...
#
# function: usage ([error-message])
# print out the usage lines and exit
# takes an options error meeeage to print at the
# end of the usage line
usage ()
{
echo "mount ${USER}'s home directory from server $ALM_SERVER"
echo ""
echo "Usage: ${0##*/} [-p password] [-h host] [-u user]"
echo " [-p password] - use an AFP password if required"
echo " [-h host] - override $ALM_SERVER as server to mount"
echo " [-u user] - override $USER as username to mount"
echo ""
if [ "$*" != "" ]; then echo "Error: $*."; fi
exit
}
# function: exit_if_set (current-value, parameter-description)
# called to prevent a parameter being given a
# value more than once
exit_if_set ()
{
if [ "$1" != "" ]; then
usage "$2 already has the value $1"
else
Unix_tricks.txt Page 163 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
return
fi
}
# process each parameter by looping and shifting
# $2 -> $1 each iteration
#
opt=""
while [ "$1" != "" ]
do
if [ "${1:0:1}" = "-" ]; then
# found a -<char> option introducer
case "$1" in
# these options require a value, remember option for next iteration
# error if previous iteration was also an option that required a value
"-usage") usage;;
"-p" | "-h" | "-u")
if [ "$opt" = "" ]; then
opt="$1"
else
usage "no value given for $opt"
fi
;;
# invalid option
*) usage "invalid option $1";;
esac
else
case "$opt" in
# previous iteration was option that required a value, set the value
"-p") exit_if_set "$password" "password"; password="$1";;
"-h") exit_if_set "$host" "host"; host="$1";;
"-u") exit_if_set "$user" "user"; user="$1";;
"") usage "invalid value $1";;
esac
# a value has been found for -<char>, so reset this flag
opt=""
fi
shift
done
# this case is where the last option was -<char>,
# thus no value has been given
if [ "$opt" != "" ]; then
usage "no value given for $opt"
fi
# set default values for optional parameters
if [ "$host" = "" ]; then host="$ALM_SERVER"; fi
if [ "$user" = "" ]; then user="$USER"; fi
# main processing
#
# if password is given add ":" to the start to comply
# with mount syntax
Unix_tricks.txt Page 164 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
On Jaguar (but not Panther) it is necessary the force the RPC daemon to start by ensuring the
RPCSERVER is set to -YES- in /etc/hostconfig:
$ grep RPC /etc/hostconfig
RPCSERVER=-YES-
Next, reboot the machine and set up the shares (server) and mounts (client)
Set Up NFS Shares and Mounts
To define share points on the server edit /etc/exports.
$ cat /etc/exports
/Users -alldirs -maproot=nobody -network=192.168.0.0 -mask=255.255.255.0
/Users/Shared -ro -mapall=nobody
In this example directory /Users is exported.
'-alldirs' allows any subdirectory of /Users to be mounted by a client
'-maproot=nobody' ensures that root user on the client does not have root access on the server;
ESSENTIAL, unless you know exactly what you are doing
'-network...' and '-mask...' define the IP range that is allowed to mount the share. It is
ESSENTIAL that this range includes only trusted machines (see the note below).
I have exported /Users/Shared as read only (-ro). This is accessible to ALL machines (no IP
restrictions). It is exported Read Only, and all users are mapped to the unprivileged user
'nobody'.
See 'man exports' for more details.
To define mount points on the client:
$ cat /etc/fstab
# local mounts
#
...
# NFS mounts
#
carcharoth:/Users /nfs/Carcharoth nfs -b,-i,-P 0 0
This line looks for the share /Users on the host carcharoth (you may use either a hostname or IP
address) and mounts on the directory /nfs/Carcharoth. '/nfs' must exist, but the actual
mountpoint 'Carcharoth' will be created automatically.
'nfs' says that the share is nfs
The options -b,-i -P are described in 'man mount_nfs'.
The last two ( 0 0 ) describe 'dump frequency' and the order for file system checking. They are
not used.
Note:
NFS does not use passwords for authentication, but Unix User and Group IDs. It is assumed that
these are consistent across the server and all clients. That is, a user's account on the client
machine will have the same user id and group id as their account on the server (and all other
clients). This is generally true on a proper server-client network where user account information
is held centrally on the server.
Unix_tricks.txt Page 166 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
If a client machine connects to your server, it need only have user with the same user id and
group id as a user on the server to gain full access to that user's files on the server.
Hide Commands that Include Passwords
Typing a password on a command line can pose a security risk as the password will be saved in
Bash's command line history, and worse, written to the history file.
Add this line in to your bash startup file (/etc/profile, /etc/bashrc, ~/.bash_profile, or
~/.bashrc):
declare -x HISTCONTROL="ignorespace"
A command line starting with space will not be added to the history. Thus start any command that
contains a password with space as a security precaution.
Delete Lines
This week's tips explore sed, a handy utility to execute a script of editing commands against its
input.
Delete blank lines from a file.
$ cat double-space
This is a file with
the lines double-spaced
and some extra blank lines
too.
Use:
$ sed '/^$/d' double-space
This is a file with
the lines double-spaced
and some extra blank lines
too.
sed works by matching each line of input against a delimited regular expression (^$ in this
example). If the line matches then sed applies the editing commands that follow. 'd' deletes the
line.
See weeks 59 and 60 for information on regular expressions, and part 6 of the Learning Centre.
To write the results back to the original file use this trick:
$ sed '/^$/d' double-space > tmp; mv tmp double-space
Delete lines containing specific text. Use the same technique with an appropriate regular
expression. This example deletes all lines starting with '$'.
$ sed '/^\$/d' text
Search for Lines
Use sed to search and print matching lines, grep-style.
sed normally echoes each input line to its output. Option '-n' suppresses this. Editing command
'p' prints each matched line. Combining the two tells sed to print just matched lines.
Unix_tricks.txt Page 167 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ cat text
This file has a number
of lines, some of witch contain
the word witch, and some
that do not.
$ sed -n '/witch/p' text
of lines, some of witch contain
the word witch, and some
Beyond grep, you may wish to edit the file contents and display just the edited lines:
$ sed -n 's/witch/which/p' text
of lines, some of which contain
the word which, and some
Line Ranges
Edit a range of lines using sed.
Previous examples have used a regular expression to match lines to be edited. sed can be made to
operate on a range of lines, or a single line.
Delete all lines between begin-marker and end-marker inclusive.
$ sed '/begin-marker/,/end-marker/d' filename
Or print them:
$ sed -n '/begin-marker/,/end-marker/p' filename
Or edit them:
$ sed '/begin-marker/,/end-marker/s/witch/which/' filename
If you expect the pattern 'witch' to occur more than once on a line use option 'g' (global) to
replace all occurrences on the line.
$ sed '/begin-marker/,/end-marker/s/witch/which/g' filename
Multiple Edits
Apply more than one editing command to a matched line.
Specify multiple commands to sed using one of two techniques.
Option -e:
$ cat this
this is this
and
that is that
$ sed -e 's/this/that/g' -e 's/that/twit/g' this
twit is twit
and
twit is twit
Separate multiple commands with ';':
$ sed 's/this/that/g;s/that/twit/g' this
twit is twit
and
twit is twit
Unix_tricks.txt Page 168 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Script Files
Use a script file if the sed script becomes long or complex.
Write the script in a normal text file putting each command on a new line. Use option '-f' to
specify a scrip file.
$ cat this
this is this
and
that is that
$ cat script
s/this/that/g
s/that/twit/g
$ sed -f script this
twit is twit
and
twit is twit
Print/Delete Lines
This week's tips explore Awk, a handy utility that executes a script of editing commands against
its input. Also take a look at Week 85 - The Sed Stream Editor: sed has similar capabilities.
Print lines containing specific text. This example prints all lines starting with '$'.
$ awk '/^\$/{print($0)}' text
Delete blank lines from a file.
$ awk '/.+/{print($0)}' double-space
To write the results back to the original file use this trick:
$ awk '/.+/{print($0)}' double-space > tmp; mv tmp double-space
Awk works by matching each line of input against a pattern (in these examples, regular expression
'^\$' matching a line starting with '$', and '.+' matching a line containing at least one
character). If the input line matches then Awk applies the actions that follow in {...}.
'print($0)' prints the entire line, as does a simple 'print'.
If no pattern is given then every line is matched. If no action is given then the input line is
printed. The above example can be simplified to:
$ awk '/^\$/' text
Note the difference between printing and deleting is a reversed regular expression.
Print Fields
Print specific fields of each matched input line.
Monday's tip used the action {print($0)}. $0 represents the entire input line. $n represents
field n. Fields are separated by white space.
A common application for this is to print specific information from the output of Unix commands
such as ls and ps.
Print just the pid number (field 1):
$ ps ax | grep "[p]ostfix" | awk '{print($1)}'
Unix_tricks.txt Page 169 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
437
Print the month, date, and filename from a long listing:
$ ls -l | awk '{print($6,$7,$9)}'
Sep 13 csv
Sep 13 double-space
Sep 13 many
Aug 30 script
Aug 30 text
Aug 30 this
Sep 13 xxx
Use printf to format the output:
$ ls -l | awk '{printf("Date: %s %s, File %s\n",$7,$6,$9)}'
Date: , File
Date: 13 Sep, File csv
Date: 13 Sep, File double-space
Date: 30 Aug, File script
Date: 30 Aug, File text
Date: 30 Aug, File this
Date: 13 Sep, File xxx
The first line 'Date: , File' results from the first line written by ls -l. This can easily be
removed by sed, grep, or awk.
To learn about printf, see:
$ man 3 printf
Line Ranges
Process a range of lines using Awk.
Previous examples have used a regular expression to match lines. Awk can be made to operate on a
range of lines, or a single line.
Print all lines between begin-marker and end-marker inclusive.
$ cat text
This file has a number
begin-marker
of lines, some of which contain
the word witch, and some which
end-marker
do not.
$ awk '/begin-marker/,/end-marker/{print $0}' text
begin-marker
of lines, some of which contain
the word witch, and some which
end-marker
Or simply:
$ awk '/begin-marker/,/end-marker/' text
Multiple Commands
Execute many commands against each input line.
Unix_tricks.txt Page 170 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Separate commands with ';' and all commands will be executed against each matched line.
To print the length of each line, and the line itself, use:
$ awk '{print(length($0)); print}' text
22
This file has a number
12
begin-marker
...
This could be done with a single print statement, but illustrates the use of multiple commands.
Extending Wednesday's tip, we can print lines that fall outside a specific range:
$ awk '/begin-marker/,/end-marker/ {next}; {print}' text
This file has a number
that do not.
Matched lines execute 'next', which skips the line. Unmatched lines do not, so 'print' is
executed. Note it is different to the first example where two commands were applied to the same
pattern match:
/pattern/{command1; command2}
Here we are applying the first command to the first pattern, and the second command to the second
pattern (which is empty to match all lines).
/pattern1/{command1}; /pattern2/{command2}
This does not work:
$ awk '/begin-marker/,/end-marker/ {next; print}' text
Multiple Patterns
Specify many patterns to match against each line:
$ cat many
line one
the second line
and the third
the fourth
line five
the six
and the last line
$ awk '/^the//^line/' many
line one
the second line
the fourth
line five
the six
Apply a different command to each pattern:
$ awk '/^the/{printf("THE: %s\n",$0)}; /^line/{printf("LINE: %s\n",$0)}' many
LINE: line one
THE: the second line
THE: the fourth
LINE: line five
THE: the six
Apply a command to lines that match one pattern or the other using the 'or' (||) operator:
$ awk '/^the/||/^line/{printf("MATCHED: %s\n",$0)}' many
MATCHED: line one
Unix_tricks.txt Page 171 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Line 3: word
Total lines in file 6
More Power
Awk is a fully-fledged programming language, with c-style statements such as 'for', 'while', and
'if'.
In triplicate:
$ cat file
line 1
line 2
$ awk '{for (i = 0; i < 3; i++) print $0}' file
line 1
line 1
line 1
line 2
line 2
line 2
This is an example using 'if' statements:
$ cat posts
Mayo 34 posts
Forbes 35 posts
Sheppard 12 posts
Trevor 345678 posts
Hollis 17 posts
$ cat awk-script
BEGIN { print "More than 34 posts"; max = 0; name = ""}
{if ($2 > 34) print $0}
{if ($2 > max) {max = $2; name = $1}}
END { printf ("Max posts %d by %s\n", max, name); print "---\n"}
$ awk -f awk-script posts
More than 34 posts
Forbes 35 posts
Trevor 345678 posts
Max posts 345678 by Trevor
---
Repair Discs
Use diskutil to verify and repair disks. To repair, the disk must not be in use, and diskutil may
have to run as root.
For example, use df to find the device node:
$ df
Filesystem Size Used Avail Use% Mounted on
/dev/disk0s3 12G 6.5G 5.9G 53% /
/dev/disk0s5 12G 3.4G 9.1G 27% /Users
/dev/disk0s7 5.9G 578M 5.3G 10% /Games
/dev/disk0s9 5.9G 21M 5.8G 1% /Music
(or 'diskutil list')
Unix_tricks.txt Page 174 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
and then:
$ diskutil verifyDisk /dev/disk0s7
Started verify/repair on disk disk0s7 Games-saruman
Checking HFS Plus volume.
Checking Extents Overflow file.
Checking Catalog file.
Checking Catalog hierarchy.
Checking volume bitmap.
Checking volume information.
The volume Games-saruman appears to be OK.
Verify/repair finished on disk disk0s7 Games-saruman
Or use the mount point (usually /Volumes/name):
$ diskutil verifyDisk /Games
Use 'diskutil repairDisk' to repair.
Repair Permissions
Use diskutil to verify and repair file permissions on the system disc. You can only specify an OS
X boot volume, and to repair, the diskutil command must be run as root.
If you encounter odd problems with system functions or applications not behaving correctly,
'Verify and Repair Permissions'.
Use df to find the device node, as for Monday's tip, or specify the mount point, and then:
$ diskutil verifyPermissions /dev/disk0s3
Started verify/repair permissions on disk disk0s3 OSX-saruman
Determining correct file permissions.
...
To repair the current classic boot volume use:
$ diskutil repairOS9Permissions
You cannot repair OS 9 permissions on this machine (No Classic folders found)
OK, so I don't have OS 9 installed :-)
Verify Preferences
Verify all those .plist preference files in ~/Library/preferences.
If an application is playing up, it may be due to a corrupted preference file. Use command
'plutil' to verify the preference file.
$ plutil -lint ~/Library/Preferences/com.apple.iChat.plist
/Users/saruman/Library/Preferences/com.apple.iChat.plist: OK
This will only check the syntax of the file, not the deeper meaning as understood by the owning
application.
To check all preferences:
$ find ~/Library/Preferences -name "*.plist" -print0 | xargs -n1 -0 plutil -lint
(This uses -print0 and -0 to cope with spaces in filenames.)
Periodic Housekeeping
OS X tidies the file system by removing old temporary files, compressing and deleting log files,
Unix_tricks.txt Page 175 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
removing old system messages, backing up the NetInfo database, updating the 'locate' database,
and more...
Periodic system maintenance is scheduled by 'cron' launching a program called 'periodic'.
% cat /etc/crontab
...
#minute hour mday month wday who command
# Run daily/weekly/monthly jobs.
15 3 * * * root periodic daily
30 4 * * 6 root periodic weekly
30 5 1 * * root periodic monthly
...
The daily script is run at 3:15 am.
The weekly script is run at 4:30 am Saturday.
The monthly script is run at 5:30 am on the first of each month.
The output from these scripts can be found in:
% ls /var/log/*.out
/var/log/daily.out /var/log/monthly.out /var/log/weekly.out
Periodic looks in /etc/periodic/parameter-given-periodic and executes all scripts in the
directory.
Run the maintenance scripts manually:
% sudo periodic daily
% sudo periodic weekly
% sudo periodic monthly
Alternatively, edit /etc/crontab and choose times at which your Mac will be running:
If you wish to add your own maintenance tasks - daily, weekly, or monthly - then create the
following files as necessary.
/etc/daily.local
/etc/weekly.local
/etc/monthly.local
These are regular shell scripts, and once created are run automatically by the default daily,
weekly, and monthly scripts.
These files will not be overwritten by system updates which might otherwise affect the files in
/etc/periodic.
File System Monitoring Commands
Use commands to monitor what is happening in the file system.
Use fs_usage to report system calls in real-time. For example, to report all fs activity to a log
file:
$ fs_usage -f filesys > log
To monitor just fs activity of the posfix master daemon:
$ fs_usage -w -f filesys master
Unix_tricks.txt Page 176 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
{
// create a new session and connection to the 'users' database
$this->session = new Sess_User();
$this->user = new DB_Forums_Users('CONF_SITE_DBNAME', 'CONF_SITE_DBPASS');
// get the user's nickname, if any
$nickname = $this->session->name();
//xxxxx
// // if the user has a nickname, check they still exist in the user's databse
// // and if not, log them out and remove their session information
// if ($nickname != '') {
// if (! $this->user->isRegistered($nickname)) {
// $this->session->logout(SESS_FORGET);
// }
// }
//yyyyy
}
To remove the comments use:
$ sed '/xxxx/,/yyyy/s/^\/\///;/xxxx/d;/yyyy/d' code.php
(It is also possible, but not as easy, to come up with a sed command to remove the comments, but
keep in the xxxx and yyyy lines in but commented out.)
Case Insensitivity
Make sed pattern matches case insensitive.
Normally, sed pattern matching is case sensitive.
$ cat case
THIS IS UPPER CASE
this is lower case
$ sed -n '/CASE/p' case
THIS IS UPPER CASE
$ sed -n '/case/p' case
this is lower case
Use a script like this to match any case:
$ cat case1.script
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
/case/p
$ sed -n -f case1.script case
this is upper case
this is lower case
y translates letters in the first // to those in the second //, thus making the input line all
lowercase.
This matches and prints both lines, but unfortunately mangles the original line. If the original
line needs to be kept, we can use function lists and sed's holding space - see Friday's tip.
Unix_tricks.txt Page 179 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Function Lists
Use more complex operations on a matched line with function lists.
The script from Thursday can be improved:
$ cat case2.script
h
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
/case/ {
x
p
}
$ sed -n -f case.script case
THIS IS UPPER CASE
this is lower case
h copies the current line to the pattern holding space.
y translates letters in the first // to those in the second //, thus making the input line all
lowercase.
/case/ {...} matches all lines containing 'case' and executes the commands in {...} on those
lines
x swaps the holding space and the current line - so having matched in a case insensitive manner
we replace the mangled line with the original
p, as usual, prints the line.
Odd Behavior in Shell Conditions
This week highlights a few common problems with shell scripts and commands, illustrating where
the problem lies and how to correct it.
A command called 'test', aka '[', allows one to make all kinds of tests and comparisons. They can
involve files, strings, and numbers.
See Week 82 and:
$ man test
or
$ man [
One can compare strings to test which 'comes first', alphabetically speaking:
$ if [ "aaa" > "zzz" ]; then echo "Yes"; else echo "No"; fi
Yes
So why 'Yes'? "aaa" is not > "zzz".
The shell interprets '>' as a redirection character, and so it must be escaped:
$ if [ "aaa" \> "zzz" ]; then echo "Yes"; else echo "No"; fi
No
The first version would have created the file "zzz". Perhaps if we had tried this next example
first, it would have given a clue as to what was happening.
$ if [ "zzz" < "aaa" ]; then echo "Yes"; else echo "No"; fi
Unix_tricks.txt Page 180 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The next examples are more long-winded, but also work. You can see why they work if you
understood the explanation above.
$ if (($((2<7))==1)); then echo "Yes"; else echo "No"; fi
Yes
$ if [ $((2<7)) -eq 1 ]; then echo "Yes"; else echo "No"; fi
Yes
$ if [ $((7<2)) -eq 1 ]; then echo "Yes"; else echo "No"; fi
No
Odd Behavior in Filenames
I tried this command:
$ cd ~/Library/Preference
$ /bin/ls *.plist
ls: illegal option -- .
usage: ls [-ABCFGHLPRSTWZabcdfghiklnoqrstuvx1] [file ...]
Odd - what is happening here? The clue lies in the 'illegal option' message. I have a file there
called:
-BringToFront.plist
The shell expands *.plist. Whenever a command includes an unescaped *, it is the shell that
interprets and expands it, not the command it is being passed to. So the command seen by 'ls' is:
ls -BringToFront.plist Fire.plist Vim.plist ...
-BringToFront.plist appears to 'ls' to be a list of options.
So how to get around this? Obviously the '*' cannot be escaped: it won't be expanded by the
shell, and 'ls' will complain.
$ ls "*.plist"
ls: *.plist: No such file or directory
Most commands take the special option '--' which says 'end of options'. Anything following is
interpreted as an argument, not an option.
$ ls -- *.plist
-BringToFront.plist
Fire.plist
Vim.plist
...
When specifying a filename directly one will encounter similar difficulties:
$ ls -- -*
-i
$ rm -i
usage: rm [-f | -i] [-dPRrvW] file ...
unlink fil
Either specify the filename with a path prefix to hide the leading '-', or use the -- trick
again:
$ rm ./-i
$ rm -- -i
Some versions of Unix include the primitive 'unlink' command, which takes no options and thus
doesn't get confused by filenames starting '-'...
$ unlink -i
-bash: unlink: command not found
...but not OS X.
Unix_tricks.txt Page 182 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Odd Behavior in Complex Shell Conditions
The 'test' or '[' command is able to perform 'and' and 'or'. '-a' signifies 'and', '-o' signifies
'or'. See Week 82 Friday.
It is often necessary to use ( ... ) to force the correct order of evaluation. In this example we
want:
A and (B or C)
$ if [ 1 -eq 0 -a ( 1 -eq 1 -o 1 -eq 1 ) ]; then echo "Yes"; else echo "No"; fi
-bash: syntax error near unexpected token `('
The bash shell is interpreting the brackets and not passing them to 'test'.
We could try to escape the entire expression by surrounding it with quotes:
$ if [ "1 -eq 0 -a ( 1 -eq 1 -o 1 -eq 1 )" ]; then echo "Yes"; else echo "No"; fi
Yes
This is actually the wrong answer, it should give 'no' (FALSE and (TRUE or TRUE) = FALSE). Why
does it give the wrong answer? Because we are now passing a string into 'test', not an
expression, and a string tests as TRUE (or FALSE for an empty (null) string).
The secret is to escape each bracket from the shell, so the expression expression is passed the
'test' as en expression not a string.
$ if [ 1 -eq 0 -a \( 1 -eq 1 -o 1 -eq 1 \) ]; then echo "Yes"; else echo "No"; fi
No
Odd Behavior in Shell Escaping
Here is a file that contains two instance of the sequence '!*'.
$ cat ps
This is a file
!* with two
!* sequences
in it.
Suppose we wish to run the file through grep to search for the lines containing '!*'. The first
attempt fails:
$ grep !* ps
grep ps ps
It oddly echoes 'grep ps ps' because the shell interprets '!*' as 'the parameters passed to the
last command'.
(Very useful in itself sometimes:)
$ ls a-very-long-filename-indeed-that-we-might-refer-to-in-the-next-command
a-very-long-filename-indeed-that-we-might-refer-to-in-the-next-command
$ cat !*
cat a-very-long-filename-indeed-that-we-might-refer-to-in-the-next-command
Hello
To avoid shell interpretation:
$ grep "!*" ps
grep "ps ps" ps
Still no good, because bash interprets '!' even within double quotes. See Week 83 for an
explanation of the shell's interpretation of special characters in " " and ' '.
Unix_tricks.txt Page 183 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Trying single quotes:
$ grep '!*' ps
This is a file
!* with two
!* sequences
in it.
Still no go. Remember that grep takes regular expressions. See Weeks 9, 10, 52, and others. The
'*' needs to be escaped from grep too, to say "match '*', don't interpret it as a pattern
repeater".
$ grep '!\*' ps
!* with two
!* sequences
Or:
$ grep \!\\\* ps
!* with two
!* sequences
Here we escaped:
'!' from the shell with '\!'
'*' from the shell with '\*'
Remember that '\' is a special character to the shell, so to send '\*' to grep we need to escape
both with '\\' and '\*'. Hence '\\\*'.
NetInfo Commands
This week presents some useful scripts to manage user accounts on the command line - add a new
user, a new group, and add existing users to a group.
There are many commands to query and change the NetInfo database, two of the most useful being:
dscl - Directory Services Command Line
A replacement for the older niutil that manipulates the information in NetInfo, and in fact in
any Directory Services node.
For example, to add a new user:
$ dscl . create /users/joe
and to add property-value pairs:
$ dscl . create /users/joe name joe
$ dscl . create /users/joe shell /bin/bash
$ dscl . create /users/joe realname "joe jones"
Any NetInfo subdirectory can be created, and property-value pairs added, in a similar manner.
nireport - Print tables from NetInfo
This reports on the values of the listed properties for specified NetInfo directories.
For example, list all users:
$ nireport . /users name
nobody
root
daemon
unknown
Unix_tricks.txt Page 184 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
smmsp
lp
postfix
...
List all group names and their group IDs.
$ nireport . /groups name gid
nobody -2
nogroup -1
wheel 0
daemon 1
kmem 2
sys 3
tty 4
...
Other commands are:
nifind, nigrep, niload, nidump
In single user mode, when NetInfo is not running, use nicl.
These are all documented in the Unix manual.
Tuesday gives some handy one-lines for querying the NetInfo database, while Wednesday, Thursday,
and Friday give scripts to add new users and groups to NetInfo, and add users to groups.
Query NetInfo
Here are some tips on using nireport to extract information on users and groups from NetInfo.
These examples can be used in Bash scripts.
Check if a user exists:
$ user=jan
$ if [ ! -z "$(nireport . /users name | grep -w $user)" ]; then echo "Exists"; fi
Exists
$ user=xxxx
$ if [ ! -z "$(nireport . /users name | grep -w $user)" ]; then echo "Exists"; fi
root@saruman ~/bin
$
Check if a user is in a group (users root then jan in group admin):
$ group=admin
$ user=root
$ in=$(nireport . /groups name users | grep -w "$group.*$user"); if [ ! -z "$in" ]; then echo "In
group"; fi
In group
$ user=jan
$ in=$(nireport . /groups name users | grep -w "$group.*$user"); if [ ! -z "$in" ]; then echo "In
group"; fi
$
Convert a group name to GID:
$ group=admin
$ echo "$(nireport . /groups gid name | grep $group | cut -f 1)"
80
Check if this is the user's primary group:
Unix_tricks.txt Page 185 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ user=jan
$ group=jan
$ gid="$(nireport . /groups gid name | grep $group | cut -f 1)"
$ pri=$(nireport . /users name gid | grep -w "$user[[:space:]].*$gid"); if [ ! -z "$pri" ]; then
echo "Primary group"; fi
Primary group
$ user=jan
$ group=admin
$ gid="$(nireport . /groups gid name | grep $group | cut -f 1)"
$ pri=$(nireport . /users name gid | grep -w "$user[[:space:]].*$gid"); if [ ! -z "$pri" ]; then
echo "Primary group"; fi
$
Get the UID and GID for a given user:
$ user=jan
$ nireport . /users name uid gid | grep $user
jan 520 520
Add a New User
This script adds a new user account to OS X. Give the user's first and last names, the desired
user id, and if they are to be a normal (staff) or admin user.
The user id is supplied because normal account creation in OS X does not give this option. The
user's short name will be equal to their first name. A home directory will be created so the user
is a fully-fledged OS X account holder and can log in in the normal manner.
The script will prompt for a password for the new user.
Lots of checks are made, as you can see from the script comments.
Grab the script from here.
NOTE: THIS SCRIPT IS WRITTEN FOR PANTHER (10.3)
#!/bin/bash
# Create a user.
# Takes the user's firstname (=shortname), lastname, uid, and staff|admin
# and creates:
# a new user in NetInfo passwd
# a new /Users/firstname home directory
usage ()
{
echo "Create a new staff or admin user"
echo "Usage: ${0##*/} firstname lastname uid staff|admin"
if [ "$*" != "" ]; then echo " Error: $*"; fi
exit 1
}
Unix_tricks.txt Page 186 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Add the new user to NetInfo
#
# add user and essential properties
dscl . create /users/$first
dscl . create /users/$first name $first
dscl . create /users/$first passwd "*"
dscl . create /users/$first hint ""
dscl . create /users/$first uid $uid
dscl . create /users/$first gid $uid
dscl . create /users/$first home /Users/$first
dscl . create /users/$first shell /bin/bash
dscl . create /users/$first realname "$first $last"
dscl . create /users/$first picture "/Library/User Pictures/Fun/Smack.tif"
dscl . create /users/$first sharedDir Public
# add some other properties that are usually in NetInfo
dscl . create /users/$first _shadow_passwd ""
dscl . create /users/$first _writers_hint $first
dscl . create /users/$first _writers_real_name $first
# add the new group
dscl . create /groups/$first
dscl . create /groups/$first name $first
dscl . create /groups/$first passwd "*"
dscl . create /groups/$first gid $uid
echo "New user and group $first created"
# Add admin users to the admin group
#
if [ $4 = admin ]; then
dscl . merge /groups/admin users $first
dscl . merge /groups/appserverusr users $first
dscl . merge /groups/appserveradm users $first
echo "$first added to groups admin, appserverusr, appserveradm"
fi
# Create the home directory, populate from the template, and set owners
#
mkdir /Users/$first
if [ ! -d /Users/$first ]; then
echo "Unable to create the user's home directory /Users/$first"
exit
fi
ditto -rsrc /System/Library/User\ Template/English.lproj/ /Users/$first
chown -R ${first}:$first /Users/$first
echo "Home directory /Users/$first created and populated"
Unix_tricks.txt Page 188 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Now give the user a password
#
echo "A password for this account must be given, it is currently blank"
passwd $first
exit 0
Add a New Group
This script adds a new group to OS X. Give the group name and id. The group will be created
without any users belonging to it. Add users to the group with add-user2group (see Friday).
Lots of checks are made, as you can see from the script comments.
Grab the script from here.
NOTE: THIS SCRIPT IS WRITTEN FOR PANTHER (10.3)
#!/bin/bash
# Create a group.
# Takes a group name and gid and creates a new group in NetInfo groups
usage ()
{
echo "Create a new group"
echo "Usage: ${0##*/} groupname gid"
if [ "$*" != "" ]; then echo " Error: $*"; fi
exit 1
}
# The script must be run as root
#
if [ "$USER" != "root" ]; then
echo "Must be run as root."
exit 1
fi
# Check parameters
#
if [ $# -ne 2 ]; then
usage
fi
Unix_tricks.txt Page 189 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
group=$1; gid=$2
# search NetInfo for the given group - it should not exist
str="$(nireport . /groups name | grep -w $group)"
if [ ! -z "$str" ]; then
usage "Group $group already exists"
fi
# search NetInfo for the given gid - it should not exist
str="$(nireport . /groups gid | grep -w $gid)"
if [ ! -z "$str" ]; then
usage "Group ID $gid already exists"
fi
# Add the new group to NetInfo
#
# add group and essential properties
dscl . create /groups/$group
dscl . create /groups/$group name $group
dscl . create /groups/$group passwd "*"
dscl . create /groups/$group gid $gid
#dscl . create /groups/$group users "" breaks add-user2group if added as a blank value
echo "New group $group created"
echo "Now add users to it with add-user2group"
exit 0
Add Users to a Group
This script adds one or many users to a group. Give the group names and a list of user short
names.
Lots of checks are made, as you can see from the script comments.
Grab the script from here.
NOTE: THIS SCRIPT IS WRITTEN FOR PANTHER (10.3)
#!/bin/bash
# Add new users to a group.
# Adds a user (or many users) to an existing group in NetInfo
usage ()
{
echo "Add a user (or several users) to an existing group"
echo "Usage: ${0##*/} group user [user...]"
if [ "$*" != "" ]; then echo " Error: $*"; fi
exit 1
}
Unix_tricks.txt Page 190 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Ensure user is root
#
if [ "$USER" != "root" ]; then
echo "Must be run as root."
exit 1
fi
# Check parameters
#
if [ $# -lt 2 ]; then
usage
fi
group=$1
# search NetInfo for the given group - it should exist
str="$(nireport . /groups name | grep -w $group)"
if [ -z "$str" ]; then
usage "Group $group does not exist"
fi
# get the group number from the name
gid="$(nireport . /groups gid name | grep $group | cut -f 1)"
# Drop the group and loop thro' additional parameters (users) to add to group
#
shift
for user in $*; do
# check if the user exists
struser="$(nireport . /users name | grep -w $user)"
# check if the user already belongs to the group
stringroup=$(nireport . /groups name users | grep -w "$group[[:space:]].*$user")
# check if this is the user's primary group
strprimary=$(nireport . /users name gid | grep -w "$user[[:space:]].*$gid")
#echo "user $struser, ingroup $stringroup, primary $strprimary"
# ensure that the user exists...
if [ -z "$struser" ]; then
echo "User $user does not exist"
# ...and does not already belong to the group...
elif [ ! -z "$stringroup" ]; then
echo "User $user already belongs to group $group - not added again"
# ...and this is not the user's primary group
elif [ ! -z "$strprimary" ]; then
Unix_tricks.txt Page 191 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Understand quotes within quotes.
Suppose the volume name included spaces (or shell special characters).
$ volname="New Games"
$ while [ -z "$(df | grep $volname)" ]; do echo -n "."; sleep 2; done; echo
grep: Games: No such file or directory
.^C
We must quote $volname. But, as the expression:
"$(df | grep $volname)"
is itself already quoted, how can we quote $volname too? Using single quotes '...' for the outer
set will not work because the shell won't interpret '$(...)'.
You might expect this to work:
$ while [ -z "$(df | grep \"$volname\")" ]; do echo -n "."; sleep 2; done; echo
grep: Trailing backslash
.grep: Trailing backslash
It doesn't because Bash is being quite clever. Within "$(...)" Bash re-sets its idea of nested
quotes, and the very simple solution is:
$ while [ -z "$(df | grep "$volname")" ]; do echo -n "."; sleep 2; done; echo
......
Quotes in Quotes
This useful trick is not always obvious until it is pointed out.
These should be obvious:
$ word=Hi
$ echo "He said $word"
He said Hi
$ echo $word
Hi
$ echo '$word'
$word
What's the output from this command, and why?
$ echo "He said '$word'"
Test for a Numeric Variable
Bash does not seem to provide an easy way of testing if a variable is numeric ('123' as opposed
to 'abc' or 'a2').
Here are two indirect ways of making this test. If you know of a better method let me know.
The first technique uses grep and a regular expression to search for a string of all digits. If
the string is not all digits grep returns a null string, which is interpreted as false.
$ var=12
$ if [ "$(echo $var | grep "^[[:digit:]]*$")" ]; then echo number; fi
number
The second technique relies on 'test' (or '[') throwing an error and returning '2' if -eq is
Unix_tricks.txt Page 193 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
given a non numeric value. Otherwise '0' or '1' is returned for TRUE or FALSE.
$ var="123"
$ [ $var -eq 0 ] 2> /dev/null
$ if [ $? -eq 0 -o $? -eq 1 ]; then echo number; fi
number
$
$ var="12AB"
$ [ $var -eq 0 ] 2> /dev/null
$ if [ $? -eq 0 -o $? -eq 1 ]; then echo number; fi
$
Variable Variables
Bash can implement variables variables, in a round-about way. PHP programmers will be familiar
with variable variables.
Basically, we want to construct a variable name from another variable:
From:
$var=BSD
We want to create a variable called:
system_BSD
Let's set to 'yes' variable 'system_???', where ??? is the value of $var:
$ var=BSD
$ system_$var=yes
-bash: system_BSD=yes: command not found
This fails because Bash interprets $var as BSD, then treats the resulting 'system_BSD=yes' as a
command. We need to evaluate $BSD, then tell Bash to re-parse the line from scratch using 'eval'.
To set a variable variable use:
$ var=BSD
$ eval "system_$var=yes"
$ echo $system_BSD
yes
We can't echo the value in the same manner:
$ eval "echo $system_$var"
BSD
We want $var to be 'eval'uated, but not $system:
$ eval "echo \$system_$var"
yes
$
Underline a String
Underline a generated string, which may be of any length.
$ cat ./underline
#!/bin/bash
read -p 'Give a string to underline: ' string
Unix_tricks.txt Page 194 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
len=${#string}
echo $string
while ((len != 0)); do
echo -n "-"
((len = len - 1))
done
echo
$ ./underline
Give a string to underline: This is a test.
This is a test.
---------------
The construct:
${#var}
returns the length in characters of 'var'.
This little script uses Bash built-in integer tests and arithmetic:
while ((len != 0)); do
((len = len - 1))
Notice anything unusual/unexpected?
'echo -n' is used so as not to follow each dash with a newline.
This example would normally be coded as a Bash function and called whenever the main script needs
to underline a string.
Return Arbitrary Values
Return an arbitrary value from a Bash function.
Bash functions return an exit status between 0 and 255. This is testable via the shell special
variable $?. But how do we return an arbitrary number, or even a string?
One way to do this is to get the function to 'echo' the return value, and use the $(...)
construct to capture the output from the function. $(...) is usually used to capture the output
from commands, but it works equally well for functions.
Here I have converted the underline example from Monday's tip into a function that returns the
underline string.
$ cat ./underline
#!/bin/bash
# Function underline
#
# Generate an underline string
#
# $1: the string to underline
# $2: the optional underline character, default value is '-'
#
# return: the underline string
#
underline ()
Unix_tricks.txt Page 195 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
{
local -i len # to hold the length of the string in $1
local line # to hold the generated underline string
local char=${2:-"-"} # set the underline to $2 or the default if not supplied
len=${#1}
while ((len != 0)); do
line=${line}$char
((len = len - 1))
done
echo $line
}
# Main script
#
read -p 'Give a string to underline: ' string
echo $string
dashes=$(underline "$string")
echo $dashes
$ ./underline
Give a string to underline: This is a test...
This is a test...
-----------------
Function underline returns its value with:
echo $line
and this value is captured in 'dashes' by the main script using:
dashes=$(underline "$string")
This is a useful construct:
char=${2:-"-"}
Return many values using arrays.
Here is a simple example using an array to receive many return values from a function. 'count'
returns four strings.
$ cat numbers
#!/bin/bash
# Function count
#
count ()
{
#return one two three four
echo one two three four
}
Unix_tricks.txt Page 196 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Main
#
declare -a numbers
numbers=($(count))
echo ${numbers[0]}
echo ${numbers[1]}
echo ${numbers[2]}
echo ${numbers[3]}
$ ./numbers
one
two
three
four
Stop Echoing for Passwords
Stop input being echoed when a script reads a password.
A script can print a prompt, read text from the user, but the text is echoed. If a password is
being read, it would be nice to prevent this from being echoed.
The secret is to use:
stty -echo
to switch off echoing.
$ cat getpass
#!/bin/bash
read -p "Give your name: " name
stty -echo
read -p "And your password: " pass; echo
stty echo
read -p "And your shoe size: " ss
$ ./getpass
Give your name: Adrian
And your password:
And your shoe size: 9
Access the Last Parameter
Get the value of the last parameter passed to a script/function when the number of parameters is
*not* known.
$2 id the second parameter, but how do we get the n'th parameter without knowing 'n'?
The number of parameters is given by the special parameter $#. We need '$$#'.
Unix_tricks.txt Page 197 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
ftp>...
ftp> quit
221-
Data traffic for this session was 0 bytes in 0 files.
Total traffic for this session was 480 bytes in 0 transfers.
221 Thank you for using the FTP service on carcharoth.mayo-family.com.
Allow and Deny Explicitly
Disallow FTP access by default.
The standard Apple-supplied ftp configuration allows all users to use FTP, except those
explicitly listed - i.e. allow by default and deny explicitly. A more security-conscious set-up
would be to deny by default and allow explicitly.
Change the /etc/ftpusers file from:
$ cat /etc/ftpusers
# list of users disallowed any ftp access.
# read by ftpd(8).
Administrator
administrator
root
uucp
daemon
unknown
www
to:
$ cat /etc/ftpusers
# list of users allowed ftp access.
# read by ftpd(8).
saruman allow
loraine allow
... list all the accounts that may ftp...
# deny all other users
* deny
This file must be edited as root, or via 'sudo'.
Jail and Umask
Jail users to their home directory.
The standard Apple-supplied ftp configuration allows all users the same access to the file system
they would enjoy by when logging in. That is, they can 'cd /' and view many system files. A more
security-conscious set-up would 'jail' most users such that 'cd /' took them to their home
directory. For theses users, their home directory is the root of their file system and they can
never move to a level above it.
Edit/create the file /etc/ftpchroot:
$ sudo pico /etc/ftpchroot
so that it looks like this:
$ cat /etc/ftpchroot
# all users are chrooted
*
Unix_tricks.txt Page 200 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Tomorrow's tip shows how to jail some users, and not others (without explicitly listing all
chrooted users).
Change the default umask.
The default ftp umask in 027, as opposed to the more usual 022. This means that users uploading
files to be served by Apache, for example, create files that Apache cannot read.
FTP provides a 'chmod' command:
ftp> help chmod
chmod change file permissions of remote file
but a more convenient solution may be to change the default umask for all users to be 022.
Edit/create the file /etc/ftpd.conf:
$ sudo pico /etc/ftpd.conf
so that it looks like:
$ cat /etc/ftpd.conf
# normally 027
umask all 022
Test with:
$ ftp loraine@carcharoth
Connected to carcharoth.mayo-family.com.
....
230 User loraine logged in.
ftp> umask
200 Current UMASK is 022
ftp> quit
...
$
Define Classes of Users
To get finer control over FTP access, umask settings, chrooting, etc, FTP allows one to define
classes. Settings can be defined against a class, and each user assigned to the appropriate
class. A user will then inherit the attributes set for the class to which they belong.
Edit the ftp config file to define classes.
$ sudo pico /etc/ftpd.conf
In the configuration below, I have defined two classes, 'free' and 'restricted'. I've 'chrooted'
the free class to '/' so they can view the entire file system (the default setting). Restricted
users can only view their home directory (%d). Note that 'homedir' for restricted users is set to
'/', which is their home directory because of their chroot setting.
$ cat /etc/ftpd.conf
# users of class 'free' (see /etc/ftpusers) chroot to /
# with their ftp home directory set to their login home
chroot free /
homedir free %d
# users of class 'restricted' chroot to their home directory
# with their ftp home directory set to their new root (ie their login home)
chroot restricted %d
homedir restricted /
Unix_tricks.txt Page 201 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# change the umask from the default of 027
umask all 022
The last directive sets the umask for 'all', which represents all classes.
Security
FTP is not the most secure of services. Set up carefully it should be fine, but remember that
passwords and all communications are sent unencrypted.
If you run ssh (Secure SHell) then you might like to take advantage of secure copy and secure
FTP. You must already have ssh set up in order to use these services.
scp is like the regular Unix copy, but copies across networks using an ssh login. To copy the
file 'test' in my home directory to the host saruman.wless I can use:
$ scp ~/test saruman.wless:~/test
To use scp precede the path on the remote machine with 'hostname:' or 'ip-address:'.
The equivalent of 'ftp' is 'sftp'. As far as I know it is not possible to configure sftp to the
same degree as ftp. For example, it is not possible to jail users to their home directories.
Note: To use scp ensure that file /etc/sshd_config has the line:
# override default of no subsystems
Subsystem sftp /usr/libexec/sftp-server
Finally, to restrict the range of ports to which a client can call-back, use (for example):
# set port range for passive for all classes
portrange all 40000 40999
The default is 1024-65535. After having made this change you can then restrict the number of open
ports in the firewall configuration (see Monday's tip) to:
03010 allow tcp from any 20,21 to any 40000-40999 in
Strings and Vis
Use 'strings' to remove junk characters.
The strings command searches a file for printable strings and displays them. It can be used on a
text file to remove spurious non-printable characters.
$ strings text-file > text-file.clean
Peek inside commands.
Use strings on commands such as 'ls' to discover embedded text:
$ strings /bin/ls
__dyld_mod_term_funcs
__dyld_make_delayed_module_initializer_calls
The kernel support for the dynamic linker is not present to run this program.
$FreeBSD: src/bin/ls/cmp.c,v 1.12 2002/06/30 05:13:54 obrien Exp $
@(#) Copyright (c) 1989, 1993, 1994
The Regents of the University of California. All rights reserved.
$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $
COLUMNS
CLICOLOR
1ABCFGHLPRSTWZabcdfghiklmnopqrstuvwx
...
Unix_tricks.txt Page 202 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Use 'vis' to display messy files.
Vis displays control characters as escape sequences allowing you to view files that might
otherwise spew junk to the terminal.
File and Hexdump
Use 'file' to discover file types.
$ file safari-bm.html
safari-bm.html: UTF-8 Unicode English text, with very long lines
$ file safari-bm.txt
safari-bm.txt: OS/2 URL object text (WWW) <http://mayo-family.com/
http://album>
$ file /bin/ls
/bin/ls: Mach-O executable ppc
$ file /
/: sticky directory
$ file ~
/Users/saruman: directory
$ file /dev/disk0
/dev/disk0: block special (14/0)
$ file /dev/rdisk0
/dev/rdisk0: character special (14/0)
and so on...
Use 'hexdump' to display a file in hexadecimal.
$ hexdump /bin/ls | head -n 5
0000000 feed face 0000 0012 0000 0000 0000 0002
0000010 0000 000f 0000 0778 0000 0095 0000 0001
0000020 0000 0038 5f5f 5041 4745 5a45 524f 0000
0000030 0000 0000 0000 0000 0000 1000 0000 0000
0000040 0000 0000 0000 0000 0000 0000 0000 0000
hexdump is useful for revealing control characters in text files, or examiming the contents of a
binary file
Note that Mach-O executables start with the hex sequence 'feed face' :-)
When is it Time to Leave?
Use 'leave' so you never again forget lunch.
Lunch at one:
$ leave 0100
Alarm set for Mon Dec 27 13:00. (pid 10494)
Coffee in 10 minutes:
$ leave +10
Alarm set for Mon Dec 27 12:38. (pid 10497)
Unix_tricks.txt Page 203 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Leave will remind you periodically about the forthcoming event, and with increasing frequency as
the event approaches.
$ You have to leave in 5 minutes.
...
$ Just one more minute!
...
Time to leave!
...
Time to leave!
...
Time to leave!
...
When you log out the nagging will finish. If you don't log out, kill leave using its pid as
echoed back when the reminder was set.
$ kill -KILL 10494
To kill all leaves use:
$ kill -KILL $(ps | grep ' [l]eave ' | awk '{print $1}')
(in tcsh use:)
$ kill -KILL `ps | grep ' [l]eave ' | awk '{print $1}'`
Record a Terminal Session
Use 'script' to record a sequence of commands.
If you are doing something you may want to repeat later, or wish others to do, use the script
command to record you terminal session to a file (~/typescript if no filename is given). Option
-a appends to an existing script results file.
$ script
Script started, output file is typescript
$ cd ~
$ ls
Desktop Documents Movies Pictures Sites osxfaq test.clean
Development Library Music Public bin test typescript
$ rm test
$ rm test.clean
(at this point press control-D to exit the script session)
$ exit
Script done, output file is typescript
View the script file:
$ cat ~/typescript
Script started on Mon Dec 27 12:45:12 2004
$ cd ~
$ ls
Desktop Documents Movies Pictures Sites osxfaq test.clean
Development Library Music Public bin test typescript
$ rm test
$ rm test.clean
$ exit
Script done on Mon Dec 27 12:45:34 2004
$
Of course, if all you want is the commands you typed, use history.
$ history -c
$ cd ~
Unix_tricks.txt Page 204 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ ls
Desktop Documents Movies Pictures Sites osxfaq .....
finish the session
$ history > ~/what-i-typed
Who's Logged In
There are several command for monitoring which users are currently logged into you Mac or server.
$ who
saruman console Dec 19 17:29
ferdi ttyp2 Dec 23 21:30 (10.0.2.1)
User 'ferdi' is logged in from another machine, as given by the IP address in brackets. You can
send that user a message direct to their console:
$ write ferdi [ttyp2]
get off
The tty number is optional if they are logged in just once.
Command 'w' gives a little more information.
$ w
21:30 up 6 days, 2:36, 2 users, load averages: 0.45 0.21 0.21
USER TTY FROM LOGIN@ IDLE WHAT
saruman console - Sun17 4days -
ferdi p2 middle-earth.wle 21:30 - -bash
Command 'users' lists only local users:
$ users
saruman
Command 'last' give a history of who was, and who still is, logged in:
$ last | head -n 5
ferdi ttyp2 10.0.2.1 Thu Dec 23 21:30 still logged in
saruman ttyp2 10.0.2.1 Thu Dec 23 21:25 - 21:26 (00:00)
saruman ttyp2 10.0.2.1 Thu Dec 23 21:19 - 21:25 (00:05)
saruman ttyp3 Thu Dec 23 21:01 still logged in
saruman ttyp3 Thu Dec 23 21:01 - 21:01 (00:00)
'uptime' is a good to boast about how long your server has been up and running.
$ uptime
21:16 up 6 days, 2:22, 2 users, load averages: 0.21 0.36 0.28
Directory Service Commands
More useful scripts to manage user accounts on the command line - delete a group, delete a user
from a group, and delete a user.
First, here are some tips on commands to query the user accounts in NetInfo.
Use 'dcsl list' to display groups and users.
$ dscl . list /groups
admin
appserveradm
appserverusr
bin
claire
...
Unix_tricks.txt Page 205 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
$ dscl . list /users
appserver
claire
cyrus
daemon
...
Use 'dscl search' to check for property-value pairs.
$ dscl . search /groups name claire
Search results for dsAttrTypeStandard:name
Search results for dsAttrTypeNative:name
claire dsAttrTypeNative:name = (claire)
Use 'dscl read' to display properties and values.
$ dscl . read /groups
name: dsRecTypeNative:groups
$ dscl . read /groups/claire
AppleMetaNodeLocation: /NetInfo/DefaultLocalNode
GeneratedUID: 0066A0DA-ED62-11D8-B80E-000393B2D604
GroupMembership:
Password: *
PrimaryGroupID: 507
RecordName: claire
Use 'nifind' to check if an entry (group, user, etc) exists.
$ nifind /users/claire .
/users/claire found in ., id = 90
$ nifind /users/xxxxxxxxx .
$
Delete Group
Use this script to delete a group from NetInfo.
Grab the script from here.
$ cat del-group
#!/bin/bash
# Delete a group.
# Takes the group name and deletes it from NetInfo
declare quiet="no" # -q option not specified
declare group # hold te given group name
declare gid # hold the group id derived from the group name
declare ans # reply from prompt
usage ()
{
echo "Delete a group"
echo "Usage: ${0##*/} [-q] groupname"
echo " -q - quiet: no warnings or prompts for confirmation"
Unix_tricks.txt Page 206 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Ensure user is root
#
if [ "$USER" != "root" ]; then
echo "Must be run as root"
exit 1
fi
# Check parameters
#
if [ $# -lt 2 ]; then
usage
fi
groups="$1"; user="$2"
# If group is all, expand into the list of groups to which the user belongs
#
if [ $groups = "all" ]; then
groups="$(id -Gnr $user)"
fi
# Loop to remove the user from each group
#
for group in $groups; do
# get the group number from the name
gid="$(nireport . /groups name gid | grep -w "^$group" | cut -f 2)"
# check if the group exists
strgroup="$(nifind /groups/$group .)"
# check if the user is listed for the group (not listed in own primary)
stringroup="$(nireport . /groups name users | grep -w "^$group[[:space:]].*$user")"
# check if this is the user's primary group
strprimary="$(nireport . /users name gid | grep -w "^$user[[:space:]]*$gid")"
# ensure that the group exists...
if [ -z "$strgroup" ]; then
echo "Group $group does not exist"
# ...and this is not the user's primary group
elif [ ! -z "$strprimary" ]; then
echo "Not removing from primary group $group"
# ...and that the user is listed in the group
elif [ -z "$stringroup" ]; then
echo "User $user not listed in $group"
else
# remove user from the group
dscl . delete /groups/$group users $user
Unix_tricks.txt Page 209 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
fi
groups="$1"; user="$2"
# If group is all, expand into the list of groups to which the user belongs
#
if [ $groups = "all" ]; then
groups="$(id -Gnr $user)"
fi
# Loop to remove the user from each group
#
for group in $groups; do
# get the group number from the name
gid="$(nireport . /groups name gid | grep -w "^$group" | cut -f 2)"
# check if the group exists
strgroup="$(nifind /groups/$group .)"
# check if the user is listed for the group (not listed in own primary)
stringroup="$(nireport . /groups name users | grep -w "^$group[[:space:]].*$user")"
# check if this is the user's primary group
strprimary="$(nireport . /users name gid | grep -w "^$user[[:space:]]*$gid")"
# ensure that the group exists...
if [ -z "$strgroup" ]; then
echo "Group $group does not exist"
# ...and this is not the user's primary group
elif [ ! -z "$strprimary" ]; then
echo "Not removing from primary group $group"
# ...and that the user is listed in the group
elif [ -z "$stringroup" ]; then
echo "User $user not listed in $group"
else
# remove user from the group
dscl . delete /groups/$group users $user
echo "User $user removed from group $group"
fi
done
exit 0
Delete User
Use this script to delete a user.
The user and their primary group are deleted. The user is removed from any other groups. The
users home folder is archived then deleted.
Unix_tricks.txt Page 211 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Delete the user from NetInfo
#
# delete the user from all groups
del-user4group all $user
# delete the user's primary group
del-group -q $user
# delete the user from NetInfo
dscl . delete /users/$user
echo "User $user deleted"
# Archive the user's home directory
#
# check that the user has a home directory
if [ -e /Users/$user ]; then
# archive it
cd /Users
tar -czf ${user}-archive.tgz $user
cd -
# delete it CHECKING THAT AN ARCHIVE WAS CRESATED
if [ -e /Users/${user}-archive.tgz ]; then
rm -rf /Users/${user}/
fi
fi
echo "User's home directory archived and deleted"
exit 0
Change Network Locations
Switch network locations from the command line.
The command:
$ scselect
lists all network locations, as viewed in system Preferences::Network.
The command:
$ scselect location-name
changes the current network location to the selected one.
New Since
Unix_tricks.txt Page 213 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Find those files greater than 5M in size:
$ find . -size +10000
./ferdi-coll3.psd
./ferdi-cool.psd
Find those files modified less than one day ago AND bigger than 5M:
$ find . -mtime -1 -a -size +10000
./ferdi-cool.psd
Find those files modified more than one day ago AND smaller than 5M:
$ find . -mtime +1 -a -size -10000
./ferdi-gala.psd
Find those files
modified less than one day ago AND bigger than 5M
OR
modified more than one day ago AND smaller than 5M:
$ find . \( -mtime -1 -a -size +10000 \) -o \( -mtime +1 -a -size -10000 \)
./ferdi-cool.psd
./ferdi-gala.psd
Note the use of brackets (escaped from the shell) to ensure that the AND and OR expressions are
evaluated in the correct order - that is the two ANDs are evaluated, then the results ORed.
Because 'find' assumes AND by default, the expression in this case can be shortened to:
$ find . \( -mtime -1 -size +10000 \) -o \( -mtime +1 -size -10000 \)
./ferdi-cool.psd
./ferdi-gala.psd
Also, because 'find' evaluates AND before OR, the brackets can be omitted.
$ find . -mtime -1 -size +10000 -o -mtime +1 -size -10000
./ferdi-cool.psd
./ferdi-gala.psd
If the expression were of the form:
(a -o b) -a (c -o d)
the brackets would be necessary.
Curtailing Find
Stop 'find' from traversing other file systems, recursing, and following symbolic links.
Use option '-x' to stop 'find' from looking inside mounted file systems:
$ ls /Volumes/
Inside Mac Media OSX-saruman guest
$ find -x /Volumes
/Volumes
/Volumes/guest
/Volumes/Inside Mac Media
/Volumes/OSX-saruman
Only /Volumes is searched, the three mounted file systems within it are not. Compare running this
command without option '-x'.
Find with no recursion.
$ find * -prune -size +10 -ls
Unix_tricks.txt Page 215 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
This finds all files in the current directory greater than 5K, and lists their details with
'-ls'. '-prune' stops find from recursing into each directory.
Note that:
$ find . -prune -size +10 -ls
will not work because 'find' will not recurse into the specified directory '.'.
This will work:
$ find . -maxdepth 1 -size +10 -ls
Make find follow symbolic links.
Find does not by default follow symbolic links - 'home' in this example.
$ ls -l
total 49700
... Jan 15 12:12 ferdi-coll3.psd
... Jan 23 11:59 ferdi-cool.psd
... Jan 15 12:12 ferdi-gala.psd
... Jan 23 15:59 ferdi-polish.psd
... Jan 23 16:46 home -> /Users/saruman $ find . -name "*.psd"
./ferdi-coll3.psd
./ferdi-cool.psd
./ferdi-gala.psd
./ferdi-polish.psd
Use option -L tell 'find' to follow symbolic links:
$ find -L . -name "*.psd"
./ferdi-coll3.psd
./ferdi-cool.psd
./ferdi-gala.psd
./ferdi-polish.psd
./home/Pictures/complete/ferdi-coll3.psd
./home/Pictures/complete/ferdi-cool.psd
./home/Pictures/complete/ferdi-gala.psd
./home/Pictures/complete/ferdi-polish.psd
...
Option '-H' tells 'find' to follow symbolic link given on its command line, rather than symbolic
links found during the search.
This will not follow 'home':
$ find -H . -name "*.psd"
This will:
$ find -H * -name "*.psd"
Find and Du
Find the top ten largest files/directories.
$ du -sk ~/* | sort -nr | head -n 10
2252708 /Users/saruman/Pictures
490664 /Users/saruman/Library
186164 /Users/saruman/Sites
132596 /Users/saruman/Development
46928 /Users/saruman/Documents
33216 /Users/saruman/osxfaq
14372 /Users/saruman/Movies
12304 /Users/saruman/.Trash
Unix_tricks.txt Page 216 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
10252 /Users/saruman/Desktop
580 /Users/saruman/.gimp-2.0
'du' (Disk Usage) displays the size of all files/directories (~/*). '-s' summarises presenting
just the total size for directories, 'sort -nr' sorts into numeric reverse (descending) order,
and 'head -n 10' displays just the first 10 lines.
Improve this with 'find' to include only directories.
$ find ~ -type d -maxdepth 1 -print0 | xargs -0 du -sk | sort -nr | head -n 10
3181012 /Users/saruman
2252708 /Users/saruman/Pictures
490664 /Users/saruman/Library
186164 /Users/saruman/Sites
132596 /Users/saruman/Development
46928 /Users/saruman/Documents
33216 /Users/saruman/osxfaq
14372 /Users/saruman/Movies
12304 /Users/saruman/.Trash
10252 /Users/saruman/Desktop
(options '-print0' and '-0' cater for filenames with spaces - see the man pages or previous
weeks.)
Summarise to a greater depth.
$ find ~ -type d -maxdepth 2 -print0 | xargs -0 /usr/bin/du -sk | sort -nr | head -n 20
3181012 /Users/saruman
2252708 /Users/saruman/Pictures
1772768 /Users/saruman/Pictures/iPhoto Library
490664 /Users/saruman/Library
378168 /Users/saruman/Library/Caches
308072 /Users/saruman/Pictures/complete
186164 /Users/saruman/Sites
142848 /Users/saruman/Sites/albums
132596 /Users/saruman/Development
80224 /Users/saruman/Development/developer-downloads
77160 /Users/saruman/Pictures/people
69472 /Users/saruman/Library/Mail
54920 /Users/saruman/Pictures/web-site
50224 /Users/saruman/Development/test
46928 /Users/saruman/Documents
33216 /Users/saruman/osxfaq
26304 /Users/saruman/Pictures/Photoshop-effects
20076 /Users/saruman/Library/Application Support
19100 /Users/saruman/Documents/1dot1
19012 /Users/saruman/osxfaq/sf03
Searching the Path
Use '-regex' to filer against the whole path.
Previous week's covered '-name' and '-iname' which match against names of found files. '-regex'
matches the entire path.
Find all directories called test, wherever they are in the directory hierarchy.
$ find ~ -regex ".*/test/.*"
will find:
/Users/saruman/Development/test/*
Unix_tricks.txt Page 217 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
and
/Users/saruman/Pictures/Gimp/test/*
Also, option '-E' says to use extended regular expressions, and '-iregex' says to match in a
case-insensitive manner.
Easily remove the full path name.
$ find ~ -name "*.psd" -execdir echo {} \;
ferdi-coll3.psd
ferdi-cool.psd
ferdi-gala.psd
ferdi-polish.psd
vs the regular:
$ find ~ -name "*.psd"
/Users/saruman/Development/test/find/ferdi-coll3.psd
/Users/saruman/Development/test/find/ferdi-cool.psd
/Users/saruman/Development/test/find/ferdi-gala.psd
/Users/saruman/Development/test/find/ferdi-polish.psd
...
On the Command Line
Coping with spaces in filenames is problematic for both the command line beginner and the more
experienced scripter. This week presents some solutions.
Escape spaces in filenames. To enter a filename that includes spaces on the command line use:
my\ file
"my file"
or
'my file'
Alternatively, type the first part of the filename and hit tab to auto-complete it.
Escaping can also be used to address a filename that includes odd characters such as " or \.
$ ls a*
a"b a\b
$ cat a'"'b
double quote in name
$ cat a\"b
double quote in name
$ cat a'\'b
back-slash in name
$ cat a\\b
back-slash in name
Find, Xargs, and Spaces
'Find' is a command often used to root out particular files in a directory hierarchy and process
each file, often by passing the filenames to 'xargs'.
However, it will fail for filenames with spaces:
$ ls -1 file*
file one
file two
Unix_tricks.txt Page 218 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
a list. When an intermediate variable is used the loop is fed a space-separated list.
Bash's IFS
Wednesday's example cannot be used if the list of filenames is generated by a method more complex
than wildcard expansion: for example using 'find.
This will not work:
$ cat ./find-and-loop
#!/bin/bash
list=$(find . -atime -1)
for i in $list; do
echo "File: $i"
done
$ ./find-and-loop
File: .
File: ./file
File: one
File: ./file
File: two
Neither will using:
for i in $(find . -atime -1); do
Get clever with Bash's Internal File Separator (IFS). Normally the separator is any of space,
tab, or newline. 'Find' (and 'ls' ) produce new-line separated lists, so changing the IFS to just
new-line does the trick.
$ cat find-and-loop
#!/bin/bash
list=$(find . -atime -1 )
IFS="
"
for i in $list; do
echo "File: $i"
done
$ ./find-and-loop
File: .
File: ./file one
File: ./file two
Note: There is a single newline character between the quotes in IFS="
"
Parameters With Spaces
Process parameters with spaces.
Unix_tricks.txt Page 220 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
This will not work:
$ cat params
#!/bin/bash
echo 'For $*'
for i in $*; do echo "Parameter: $i"; done
echo
echo 'For "$*"'
for i in "$*"; do echo "Parameter: $i"; done
$ ./params "param one" "param two"
For $*
Parameter: param
Parameter: one
Parameter: param
Parameter: two
For "$*"
Parameter: param one param two
Neither solution gives us:
Parameter: param one
Parameter: param two
Instead of $* to represent all parameters, we use Bash's special $@ symbol.
$ cat params
#!/bin/bash
echo 'For $@'
for i in $@; do echo "Parameter: $i"; done
echo
echo 'For "$@"'
for i in "$@"; do echo "Parameter: $i"; done
$ ./params "param one" "param two"
For $@
Parameter: param
Parameter: one
Parameter: param
Parameter: two
For "$@"
Parameter: param one
Parameter: param two
Unix_tricks.txt Page 221 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The second variant "$@" give us what we want.
Re-size and Move with Escape Sequences
Re-size and move a terminal window from the command line, or from a script, using the following
escape sequences.
Re-size to a specific number of rows and columns (eg 50 rows by 100 columns):
$ echo -n "^[[8;50;100t"
(Note: the sequence displayed as ^[ represents the escape character, obtained by typing control-v
then the 'esc' key.)
Re-size to the maximum screen width by giving columns as 0:
$ echo -n "^[[8;50;0t"
Re-size to the maximum screen height by giving rows as 0:
$ echo -n "^[[8;0;100t"
Move to a specific position (eg 10 pixels from the left, and 100 pixels from the top):
$ echo -n "^[[3;10;100;t"
When re-sizing to the maximum screen width and/or height, it is advisable to position the screen
at the top left:
$ echo -n "^[[3;0;0;t^[[8;0;0t"
Define Aliases and Functions
Define aliases and functions to move and resize. For example, to make a big screen use an alias:
(Bash)
$ alias big='echo -n "^[[3;0;0;t^[[8;0;0t"'
(tcsh)
% alias big 'echo -n "^[[3;0;0;t^[[8;0;0t"'
To size a screen use a Bash function or tcsh alias:
(Bash)
$ sz ()
> {
> echo -n "^[[8;$1;$2;t"
> }
(tcsh)
alias sz 'echo -n "^[[8;\!:1;\!:2;t"'
Then use:
$ sz 50 100 $ sz 25 80
These escape sequences work in both Apple's Terminal.app and X11 xterm.
Focus and Dock with Escape Sequences
Move a window to the background or the foreground from the command line, or within a script,
using the following escape sequences. (See Monday's tip too.)
Unix_tricks.txt Page 222 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
AppleICUDateFormatStrings = {
1 = "yyyy'.'MM'.'dd";
2 = "d' 'MMM', 'yyyy";
3 = "d' 'MMMM', 'yyyy";
4 = "EEEE', 'd' 'MMMM', 'yyyy";
...
$ defaults read -globalDomain AppleLocale
en_GB
$ defaults write -globalDomain AppleLocale en_US
$ defaults read -globalDomain AppleLocale
en_US
View a .plist file with XML decoding. This translates the XML encoding into something more
readable.
$ defaults read com.apple.iChat
{
ABDirectoryResultColumnTitle = "Instant Messaging";
AutoLogin = 1;
AutosaveChats = 1;
BuddyInfoSelectedTab = 0;
"BuddyList.SecondarySortOrder" = 1;
"BuddyList.SortOrder" = 3;
"BuddyList.Visible" = 1;
CardsBlockingPresentityPictures = (
"60765A82-A567-11D7-A842-000393B2D604:ABPerson",
"56C0BB88-C7D2-11D6-950E-000393B2D604:ABPerson"
);
...
NOTE: DO NOT WRITE THE DECODED VERSION BACK TO THE ORIGINAL .PLIST FILE DIRECTLY, use:
$ defaults write domain 'the-plist-from-defaults-read'
Use vim to view:
$ defaults read com.apple.iChat | vim -
Vim: Reading from stdin...
Use less to view:
$ defaults read com.apple.iChat | less
...
View a single property:
$ defaults read com.apple.iChat AutoLogin
1
Note: 0 = false/no, 1 = true/yes.
Change a property:
$ defaults write com.apple.iChat AutoLogin 0
$ defaults read com.apple.iChat AutoLogin
0
Note: 'defaults' automatically looks in ~/Library/Preferences and adds the .plist extension.
WARNING: don't modify the .plist of a running application.
Unix_tricks.txt Page 225 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
ApplicationServices.framework/Versions/A/
Frameworks/LaunchServices.framework/Versions/
Current/Support/lsregister
(all on one line)
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
-load Load the LaunchServices service plugin if it's not already loaded.
-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.
Issuing the command:
...the/long/path/lsregister -kill -r -domain system -domain local -domain user
will repair the Launch Services database.
I've not investigated this command much, but it does have potential for querying, maintaining,
and resetting the LS database.
Keep It Tidy
Unix is pretty much self-maintaining. A little periodic maintenance is required, and is scheduled
to run in the early hours daily, weekly, and monthly.
Scheduling is controlled by the system 'cron' scheduler, which reads its scheduling information
from /etc/crontab:
$ cat /etc/crontab
# /etc/crontab
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
HOME=/var/log
#
#minute hour mday month wday who command
#
# Run daily/weekly/monthly jobs.
15 3 * * * root periodic daily
30 4 * * 6 root periodic weekly
30 5 1 * * root periodic monthly
If your Mac is switched off during these hours you may want to re-schedule the periodic
maintenance. See:
$ man 5 crontab
Unix_tricks.txt Page 227 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
for an explanation of the format of crontab.
Alternatively, run the maintenance manually with:
$ sudo periodic daily
$ sudo periodic weekly
$ sudo periodic monthly
Other candidates for removal include log files in /var/log that are not periodically zipped and
rotated. For example, find all log files over 10 Meg:
$ find /var/log -size +20000
/var/log/named/default.log
/var/log/secure.log
Get more details with:
$ find /var/log -size +20000 -ls
Install 'anacron'. This can be obtained from the many Unix ports, including fink.
It is like cron but catches up on scheduled events that were missed when your Mac was asleep or
not running.
$ fink describe anacron
Information about 1951 packages read in 4 seconds.
anacron-2.3-4: Periodic command scheduler
Anacron executes commands at intervals specified in days. Unlike cron, it does not assume that
the system is running continuously. It's ideal for machines such as laptops...
Remove Browser caches. I run almost all browsers to test my web development. As a consequence
browser caches greatly increase the time required for incremental backups. I have written a
simple script that removes the caches for Camio, FireFox, iCab, Internet Explorer, Mozilla,
Omniweb 5, Opera 7, and Safari.
#!/bin/bash
if [ "$1" = "-usage" ]; then
echo "Cleans the caches for the browsers:"
echo "Camio, Firebird, iCab, IE, Mozilla, OmniWeb5, Opera, Safari"
echo "Usage: ${0##*/}"
exit
fi
# just in case!
cd ~/Library || exit 1
echo "Removing caches..."
# Camio
echo -n " Camio - "
{ cd ~/Library/Application\ Support/Chimera/Profiles/default/ \
&& rm -r $(ls -d *.slt | head -1)/Cache \
&& echo removed;
} 2> /dev/null \
|| echo "none"
Unix_tricks.txt Page 228 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
# Firefox
echo -n " Firefox - "
{ cd ~/Library/Application\ Support/Firefox/profiles/*default/ \
&& rm -r Cache \
&& echo removed;
} 2> /dev/null \
|| echo "none"
# iCab
echo -n " iCab - "
{ rm -r ~/Library/Preferences/iCab\ Preferences/iCab\ Cache/ \
&& echo removed;
} 2> /dev/null \
|| echo "none"
# Interner Explorer
echo -n " Internet Explorer - "
{ rm ~/Library/Caches/MS\ Internet\ Cache/IE\ Cache.waf \
&& echo removed;
} 2> /dev/null \
|| echo "none"
# Mozilla
echo -n " Mozilla - "
{ cd ~/Library/Mozilla/Profiles/default/ \
&&| rm -r $(ls -d *.slt | head -1)/Cache \
&& echo removed;
} 2> /dev/null \
|| echo "none"
# OmniWeb
echo -n " Omniweb - "
{ rm -r ~/Library/Caches/com.omnigroup.OmniWeb5/Cache.bundle \
&& echo removed;
} 2> /dev/null \
|| echo "none"
# Opera
echo -n " Opera - "
{ rm -r ~/Library/Caches/Opera\ Cache \
&& echo removed;
} 2> /dev/null \
|| echo "none"
# Safari
echo -n " Safari - "
{ rm -r ~/Library/Caches/Safari \
&& echo removed;
} 2> /dev/null \
|| echo "none"
echo "Done."
Unix_tricks.txt Page 229 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
The locations of the caches work for me, but I'm not sure with the Mozilla set of browsers how
much the random folder names vary.
Disc Drives
/Applications/Utilities/Disk Utility allows one the verify and repair permissions on the system
volume, and verify and repair the structure of other volumes. These functions can be performed
from the command line using 'diskutil'
To refer to a specific disc/volume, specify its mount point, disc identifier, or device node, as
below.
The mount point is usually /Volumes/disc-name, but in my case I have mounted directly under /.
$ diskutil verifyDisk /Music/
Started verify/repair on disk disk0s9 Music-saruman
Checking HFS Plus volume.
Checking Extents Overflow file.
Checking Catalog file.
Checking Catalog hierarchy.
Checking volume bitmap.
Checking volume information.
The volume Music-saruman appears to be OK.
Verify/repair finished on disk disk0s9 Music-saruman
To discover the disc identifier and device node use disktool.
$ disktool -l
...
***Disk Appeared ('disk0s3',Mountpoint = '/', fsType = 'hfs', volName = 'OSX-saruman')
***Disk Appeared ('disk0s5',Mountpoint = '/Users', fsType = 'hfs', volName = 'Users-saruman')
***Disk Appeared ('disk0s7',Mountpoint = '/Games', fsType = 'hfs', volName = 'Games-saruman')
***Disk Appeared ('disk0s9',Mountpoint = '/Music', fsType = 'hfs', volName = 'Music-saruman')
$ diskutil verifyDisk disk0s9
Started verify/repair on disk disk0s9 Music-saruman
Checking HFS Plus volume.
...
Verify/repair finished on disk disk0s9 Music-saruman
$ diskutil verifyDisk /dev/disk0s9
Started verify/repair on disk disk0s9 Music-saruman
Checking HFS Plus volume.
...
Verify/repair finished on disk disk0s9 Music-saruman
To repair use:
$ diskutil repairDisk /Music Started verify/repair on disk disk0s9 Music-saruman
Checking HFS Plus volume.
Checking Extents Overflow file.
Checking Catalog file.
Checking Catalog hierarchy.
Checking volume bitmap.
Checking volume information.
The volume Music-saruman appears to be OK.
Verify/repair finished on disk disk0s9 Music-saruman
Unix_tricks.txt Page 230 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Cat actually concatenates many files and writes the result to standard out (the screen)
Redirect to a file to actually join files
Option -b to number non-blank lines
Option -s to collapse multiple blank lines
Option -v to display non-printing characters so they are visible
more ... display a file in a paged manner
Scroll up and down with the cursor keys
Page up and down with the 'u' and 'd' keys
Search for text with /text-to-search-for
And lots, lots *more*, see 'man more'
less ... display a file in a paged manner
Like more, but much more than more
See 'man less'
lesskey ... specify key binding for less
Specify alternative key binding for controlling less
head ... display the first 10 lines of a file
Option -n specifies a different number of lines to display
tail ... display the last 10 lines of a file
Option -n specifies a different number of lines to display
(See tips week 4)
zcat, zmore ... cat and more zipped files
bzcat, bzmore, bzless ... cat, more, less on bzipped files
wc ... line, word, and character count files
Options -l, -w, and -c to control what is counted
file ... determine the type of a file
pr ... display files with pagination
Display files page by page with headers and footers
vis ... display files with non-printable characters
Does a better job than 'cat -v'
strings ... search a binary file for printable strings
Option -a to search the whole file, not just binary sections (applicable mainly to executables)
hexdump ... display binary files
The output shows the hexadecimal value of each byte
Option -n len to display only the first 'len' bytes
xxd ... display binary files
Like 'hexdump' but displays both the hexadecimal and character representation of each byte (or
'.' for non-printable characters)
(See tips week 95)
emacs ... powerful text editor
vi/vim ... the best text editor
(See tips weeks 50, 53, and 69)
Unix_tricks.txt Page 233 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
the command:
$ sudo periodic weekly
(See tips weeks 64, 88)
type ... find an executable (bash shell only)
Option -a to find all versions of the executable
(See tips week 77 - Tuesday)
which ... find an executable (csh and tcsh)
(See tips weeks 20 and 47 - Monday)
whereis ... find an executable (search system path)
Also:
ls -r * ... recursive directory listing
echo * ... list all files in the current directory
Commands About and For Users
id ... display identity of the current user
Option -p to display in a more readable format
Option -G to display group numbers, useful for shell scripts
groups ... display groups to which a user belongs
(See tips week 17)
logname ... display the current user's login name
finger ... display information on a user
Create ~/.plan and ~/.project files to advertise more information to finger
passwd ... change the password of the current user
chpass, chfn, chsh, ... add or change user information
Check the man pages for compatibility with OS X
umask ... change the umask for this and child processes
(See week 47 - Wednesday)
login ... log into the machine
Option -p to inherit the current environment
rlogin ... log into a remote machine
This is depreciated in favour of the more secure 'ssh' - see under 'Networking Commands' later in
the series.
su ... switch to another user's identity
sudo ... execute a command as root
Option -s to run a shell as the new user
Option -u to run as a specified user rather than root
Option -l to list what you are allowed to sudo
Options -k, -K to timeout the user's password to sudo
users ... display who is currently logged in
who ... as 'users' but with more detail
Unix_tricks.txt Page 237 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
(Bash only)
(See tips weeks 12, 20, 71, 77, 99 - Tuesday)
tee ... split a pipe into to streams
Can be used to direct the output of a command to a file and to terminal at the same time
set ... set a shell variable
unset ... remove a shell variable
setenv ... set an shell environment variable
unsetenv ... remove a shell environment variable
(tcsh only)
(See tips week 54 - Monday)
expect ... script a dialogue with interactive programs
script ... make a transcript of a terminal session
bindkey ... to change shell key functionality
batch ... execute commands in non-busy periods
The command is delayed until system load levels are low.
The load level is controlled by atrun, see Wednesday's tip.
exec ... executes command in place of shell
nohup ... execute a command immune to hangups
The command will not be stopped bu 'kill -HUP' or when the current shell exits
(See tips week 78)
lockfile ... prevent file from being accessed by more than one process
rehash ... refresh shell's executable list
(tcsh)
(See week 21 - Friday)
hashstat ... give hash hit statistics
(tcsh)
exit ... quit non-login shell
logout ... quit a login shell
Commands for Info on Machine and Discs
The last two weeks and the next two weeks will form a Unix command reference by listing lots of
commands under specific areas of usage. There are so many commands available that it is
impossible to know them all, or even remember all those one knew and used just last month.
See the rest of the tips for lots of example usage of these commands.
See the Learning Centre for tutorials on these commands, and explanations of Unix permissions.
And of course, don't forget the Unix manual 'man'.
For information on the machine (host), including hardware, operating system, and kernel use:
arch ... display the machine architecture
Unix_tricks.txt Page 240 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
lsof ... list open files
For example 'lsof' or 'lsof /dev/console'
fstat ... display file status
For example 'fstat' or 'fstat ~' or 'fsstat ~/letter.doc'
vm_stat ... display virtual memory statistics
Watch out for page-outs increasing by 100's per second: you need to buy more memory or run fewer
processes.
latency ... display context switches &interrupts
This must be run as root
dmesg ... display system message buffer
(See tips week 61 - Friday)
ac ... display user connect time for accounting
These commands operate on the kernel.
ktrace ... perform kernel tracing
For example, 'ktrace ls' executes 'ls' and trace it's kernel activity. This produces the file
'ktrace.out'.
kdump ... display a 'ktrace.out' file human readable
For example 'kdump' if the trace file is in the current directory.
Option -f to specify the trace file
zprint ... display information on kernel zones
sysctl ... display and set kernel state variables
Option -a to display all state variables and their current settings
kextload ... kernel extension load
kextunload ... kernel extension unload
kextstat ... display kernel extension statistics
Commands for Running Processes
kill ... stop/restart processes by PID
Sends a signal to a process. Give the signal number or name, and the PID (Process ID) of the
process to kill.
man 2 sigaction ... information on signals
Use this command to learn about the possible signals that 'kill' can send to running processes.
killall ... stop/restart processes by name
Like kill but a case sensitive process name is required instead of a PID.
(See tips week 45)
ps ... list running processes
Option -x to also list processes not attached to the terminal
Option -a to list processes belonging to other users
Option -ww to make a wide listing, otherwise the output is truncated
Option -c to list only the process (command) name, not the full command line
Unix_tricks.txt Page 242 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
top ... display information on running processes
(See tips week 19)
nice ... execute a process with a given priority
renice ... alter the pripority of an existing process
exec ... execute a command in place of shell
(See tips week 47)
nohup ... execute a command immune to hangups
The command will not be stopped by 'kill -HUP' or when the current shell exits
(See tips week 78)
leaks ... check process for memory leaks
For example 'leaks TextEdit'
heap ... list allocated memory in process's heap
For example 'heap TextEdit'
printenv ... display environment of the current shell
Commands for Network Fetching
curl ... grab a URL from a remote server
Supports the following protocols:
HTTP, HTTPS, FTP, GOPHER, DICT, TELNET, LDAP, FILE
For example: 'curl http://www.apple.com'
wget ... grab a URL from a remote server
Like curl, but not included with OS X. Available for OS X via ports such as Fink.
fetchmail ... get mail from SMTP, POP, IMAP etc
tin... read Usenet newsgroups
trn ... read Usenet newsgroups
ftp ... file transfer protocol
Command line ftp client to get and fetch files from remote servers
(See tips week 63)
ftpd ... ftp daemon
This runs an ftp service
(See tips week 94)
telnet ... connect using the telnet protocol
Connect to remote machines that run telnet, or connect to specific ports to test services such as
FTP, SMTP. and HTTP by hand.
ssh ... start a secure shell on a remote machine
The session is encrypted, including the transfer of password information used to log into the
remote machine.
sshd ... ssh daemon
Unix_tricks.txt Page 243 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
For example, to listen to network traffic with apple.com use 'sudo tcpdump -vvv host apple.com'
tcpflow ... dump and analyse tcp activity
Like tcpdump but not included with OS X. Available from ports such as Fink.
nslookup ... interactive domain name query (DNS)
dig ... look up DNS information
Works both as a one-off command or interactively.
host ... look up DNS information
Option -t to specify the type of DNS record to look up.
For example 'host -t ns apple.com' to fetch name server records for apple.com. 't can also be
'mx', 'a', 'soa', etc.
Option -a to request all DNS information for the host
named ... DNS daemon
This runs a DNS service
ntpd ... network time protocol daemon
This runs an ntp server
ntpq ... query ntp time server
ntptimeset ... set network time
Miscellaneous Commands
The last three weeks and the this week will form a Unix command reference by listing lots of
commands under specific areas of usage. There are so many commands available that it is
impossible to know them all, or even remember all those one knew and used just last month.
See the rest of the tips for lots of example usage of these commands.
See the Learning Centre for tutorials on these commands.
And of course, don't forget the Unix manual 'man'.
To set and display environment and shell variables. The commands are marked as (tcsh) and (bash)
if they are built-in for the respective shells, and (Unix) if they are standard Unix commands.
set ... set a shell variable (tcsh)
set ... set shell options (bash)
unset ... unset a shell variable (tcsh and bash)
setenv ... set an environment variable (tcsh)
unsetenv ... unset an environment variable (tcsh)
env ... set and display the environment (Unix)
printenv ... display environment variables (tsch and Unix)
(See tips week 54)
These commands are useful in shell scripts.
expr ... evaluate an expression
The expression can be arithmetic, relational, or logical
Used in scripting as `expr ...` or $(expr ...)
$ a=$(expr 1 + 1 )
$ echo $a
(See tips week 54 Friday
Unix_tricks.txt Page 245 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
http://www.osxfaq.com/tips/unix-tricks/week54/friday.ws
)
getopt ... get and parse command line options
This is the standard command line parsing utility used by many commands to process options and
command line arguments.
mktemp ... generate unique temporary file names
Option -d to make a directory
Option -t to include a prefix
printf ... formatted print to display
See 'man 3 printf' for more details of the formatting string.
sleep ... pause processing of a script
the script is paused for the given number of seconds to let other processes execute
[ and test ... tests an expression returning 0 (TRUE) or 1 (FALSE)
Use in conditional statements such as 'if' and 'while'
xargs ...
Option -0 the expect null-separated filenames, Used with 'find -print0' to cope with filenames
with spaces
Option -n to limit the number of arguments on each invocation. Used if the generated command line
is too long (has too many arguments).
(See tips weeks 49, 98 Thursday)
lockfile ... prevent file from being accessed by more than one process
These commands compress files and make archives.
zip unzip ... compress/uncompress files and folders
gzip gunzip ... GNU version of zip
bzip2 bunzip2 ... uses a better compression algorithm
Option -1 to -9 to adjust the level of compression,
-1 is fastest but with the least compression
(See tips weeks 5 Friday, 37, 75 Tuesday)
tar ... archive files into a single file
Option -c to create a new archive
Option -x to extract from an existing archive
Option -r/-A to append files to an existing archive
Option -z to (un)compress with gzip
Miscellaneous commands.
visudo ... safe edit of /etc/sudoers
'sudoers' should not normally be edited by a standard test editor. visudo locks the file and does
sanity checks.
vipw ... safe edit of /etc/passwd
'passwd' should not normally be edited by a standard editor. vipw locks the file and does sanity
checks.
This command is redundant as OS X uses shadow passwords and therefore does not use /etc/passwd
except in single user mode.
time ... time the execution of a command
limit/unlimit ... change system resource limits
Unix_tricks.txt Page 246 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
OSX Special - Commands for Disc and File
These commands are OS X specific and are not found in other Unix flavours.
Disc manipulation and maintenance.
disktool ... refresh, eject, mount or unmount
diskutil ... repair, journal discs
drutil ... interact with CD and DVD burners
hdiutil ... manipulate disc images
cd9660.util ... mount ISO 9660 volumes
udf.util ... mount DVD volumes
ufs.util ... mount UFS (Unix File System) volumes
hfs.util ... mount HFS (Standard Apple) volumes
pdisk ... partition table editor
List all partitions of mounted discs with:
sudo pdisk
Top level command (? for help): L
vsdbutil ... read/write enable permissions on HFS+ volumes
ditto ... copy files/sync folders with resource forks
Option -rsrc to copy preserving resource forks
cpMac ... copy with resource forks
Copies resource forks like ditto. This is part of the Developers Tool and not included in a
standard install.
GetFileInfo ... get attributes of HFS+ files
SetFile ... sets attributes of HFS+ files
Sets HFS+ attributes such as Alias, Locked, Invisible
OSX Special - System Commands
These are command line equivalents for System Preferences panes.
(See tips weeks 16 and 18)
softwareupdate ... command line version of S/W update
This is useful to software update via a remote connection to a server.
update_prebinding ...
redo_prebinding ...
When new executables and libraries are added, these commands can be used to update the binding
information so commands launch faster. Normally done as part of an install or software update.
system_profiler ... generate detailed system information
This is a command line equivalent to 'About This Mac, More Info...' in the Apple menu. It
generates textual information to standard output.
scselect ... change network location
This is equivalent to changing the Network Location in System Preferences pane Network
Unix_tricks.txt Page 247 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
pmset ... set power management parameters (screen, disc etc)
bless ...bless a system folder, optionally make it the boot disc
Can be used to change the startup disc, and to 'register' cloned systems.
nvram ... manipulation of Open Firmware NVRAM variables
ioreg ... display IO reg hierarchy
service ... interface to xinetd services - shell script
These commands query and manipulate information in NetInfo and Directory Services.
(See Tips weeks 91 and 96 for scripts which use NetInfo and Directory Service commands.)
DirectoryService ... DS daemon, part of Open Directory
ni* ... NetInfo command set to query and change NetInfo
(See example usage in Friday's tip)
nicl ... NetInfo Command Line Utility
nidump ... Dump NetInfo information in unix FF format
niload ... Load NetInfo information from unix FF format
niutil ... read and write domain in plain text
nifind ... find a NetInfo directory
nigrep ... performs a regular expression search on NetInfo
nireport ... prints tables from NetInfo
dscl ... directory services command line utility
dscl is preferred over the ni* set of commands
(See example usage in Friday's tip)
scutil ... interface to configd to manage Network Locations etc
OSX Special - Utility Commands
open ... open a file as if double-clicked
screencapture ... screen and window capture
(See example usage in Friday's tip)
These commands are for maintaining and checking Mac OS X preference files (those that end in
.plist in preferences folders).
defaults ... read and write .plist files
plutil ... .plist utilities
Miscellaneous commands.
say ... text to speach converter
sips ... scriptable image processing system
appleping ... pings and appletalk network
certtool ... manage TLS/SSL certificates and keychain access
(See 'man openssl')
opendiff ... opens two files and compare
Unix_tricks.txt Page 248 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Load the backup into NetInfo:
/usr/bin/niload -d -r / . < /var/backups/local.nidump
- Re-create NetInfo via OS X first-time setup
rm /var/db/.AppleSetupDone
reboot
Other Commands
bless
Set OS X as a startup
sudo bless -folder '/System/Library/CoreServices' -setOF
Set OS 9 as a startup
sudo bless -folder9 '/Volumes/Mac OS 9/System Folder' -setOF
defaults
defaults read/write/delete <file> <parameters>
defaults write com.apple.terminal TerminalOpaqueness '0.85';
fsck
In single user mode, to repair HFS disc 1 partition 5
fsck_hfs -y /dev/rdisk1s5
man
man 7 ascii ... ASCII table
man 7 hier ... file system hierarchy
man cmd | col -b ... print manual pages
mount
mount_afp [-o option1[,option2 ...]] afp://[username:password]@rhost[:port]/volume node
mount_cd9660 /dev/disk2s1 /Volumes/cd
umount /Volumes/cd
mount -t hfs -w /dev/disk0s10 /Volumes/Grima
nvram
nvram -p ... to print variables
sudo nvram boot-args="-v" ... set verbose mode on boot
tcpflow
sudo tcpflow -i en1 -c port 80
whois
whois -h whois.arin.net ip-address
Background Information
Bash can be started as a login shell or as a non-login shell. One of the most significant
differences between the two modes is the initialisation scripts sourced when Bash starts up.
A login shell sources the login scripts:
/etc/profile
~/.bash_profile
(If ~/.bash_profile cannot be read, ~/.bash_login is sourced, and if this can't be read,
~/.profile is sourced.)
Unix_tricks.txt Page 250 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
On exit a login shell sources ~/.bash_logout.
A non-login shell sources the following scripts:
/etc/bashrc
~/.bashrc
NOTE: according to the bash manual, /etc/bashrc is not sourced, but it is.
When Apple's Terminal.app starts a new terminal session it starts a Bash login shell. When X11's
xterm starts a new terminal session it starts a Bash non-login shell.
When you type 'bash' at the command line a non-login shell is started.
Non-interactive shells. When you run a shell script, a new shell is launched to execute the
script. The new shell is a non-interactive (non-login) shell. It does not source any scripts on
startup.
A non-login shell is exited by 'exit', and a login shell by 'logout'. Typing 'exit' in a login
shell will run 'logout'.
Make X11 Xterm Launch Login Shells
From Monday's tip it can be seen that Apple's Terminal.app launches a login shell while X11 xterm
launches a non-login shell. To help rationalise this behaviour and get a consistent environment
we can change X11 xterm to launch a login shell.
Make X11 launch login shells. Edit /etc/X11/xinit/xinitrc and change the line that invokes xterm:
xterm &
to:
xterm -ls &
Alternatively, to affect only your own account, create a file ~/.xinitrc which is a copy of
/etc/X11/xinit/xinitrc, and make the same change.
Now X11 will start a login shell.
Even better. To ensure all xterm windows are login shells too, instead of the above create a file
called ~/.Xdefaults
$ cat ~/.Xdefaults
# Default settings for X Applications
#
# xterm
XTerm*.LoginShell: True
Now xterm will start a login shell whether it is invoked by X11 on startup, or by typing 'xterm
&' on the command line, or by the X11 Applications menu.
Customise the look of an xterm terminal by adding more lines to ~/.Xdefaults. For example:
XTerm*background: black
XTerm*foreground: white
XTerm*backdrop: white
XTerm*rightScrollBar: True
XTerm*ScrollBar: True
xterm*saveLines: 5000
XTerm*VT100*geometry: 100x55+10+10
Unix_tricks.txt Page 251 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
(The reason X11 xterm launches non-login shells is that in most Unix systems X11 controls the
root window and the correct environment has already been set up. In OS X Aqua controls the root
window. Hence it is better for xterm to startup login shells so that they source the login
scripts.)
Initialisation Scripts
The four different initialisation scripts (see Monday's tip) must be set up correctly to ensure
shells have the correct environment.
First off, /etc/profile and /etc/bashrc are system-wide. Use these scripts to set up the
environment that is common to all users. ~/.bash_profile and ~/.bashrc are specific to each user
and are used to add personal settings to the environment.
Second, /etc/profile and ~/.bash_profile are sourced by login shells and therefore applied once
for each terminal session. Typing 'bash' at the command line runs a non-login shell which sources
/etc/bashrc and ~/.bashrc.
A good strategy is to do the following:
Use /etc/profile and ~/.bash_profile to set environment variables. Environment variables are
inherited by non-login shells and don't need to be setup again.
Use /etc/bashrc and ~/.bashrc to set shell variables and other shell settings. Shell variables
are not inherited by non-login shells and therefore need to be set each time.
Finally, /etc/profile should source /etc/bashrc:
source /etc/bashrc
and ~/.bash_profile should source ~/.bashrc:
source ~/.bashrc
to avoid having to set shell variables in both the login and non-login scripts. Note that these
must be 'sourced' and not simply called like:
/etc/bashrc
Prevent Execution of Initialisation Scripts
To prevent bash from running /etc/profile and ~/.bash_profile when it is invoked as a login shell
use:
bash --noprofile
To prevent a non-login shell from running /etc/bashrc and ~/.bashrc invoke on the command line
as:
bash --norc
To create a login shell from the command line use:
bash --login
To have a 'noprofile' shell option in X11, use the X11 menu bar Applications::Customise... to add
a new item. Call the item something like xterm-np and make the command:
/usr/X11R6/bin/xterm -e /bin/bash --noprofile
Note that we tell xterm to run a command with the -e option. If no option is given it runs bash
in a default manner.
Fink. Beware! If you source the Fink initialisation script, for example:
Unix_tricks.txt Page 252 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
...
Jan 1 18:11:36 Sauron kernel[0]: AFP_VFS afpfs_mount: ...
Jan 1 18:11:49 Sauron kernel[0]: AFP_VFS afpfs_unmount: ...
...
Note that in pre Tiger systems gzcat was installed as zcat and gzcat. Use gzcat in scripts for
portability.
Here's a reminder of the many z-variant commands available:
zcat zdiff zfgrep zgrep zmore zprint
zcmp zegrep zforce zless znew
Note that znew can be used to recompress .Z files to .gz files. Also included the very useful
zless, lacking in earlier versions of Mac OS X.
Remember too that bzip2 compressed files have a similar set of utilities.
bzcat bzdiff bzfgrep bzip2 bzless
bzcmp bzegrep bzgrep bzip2recover bzmore
Pico is Nano
The pico editor was a very popular and easy to learn Unix text editor. It was part of the PINE
email toolset, and as such had some critical limitations such as a lack of search and replace. It
has been replaced by the GNU nano editor. Nano is compatible with pico, but offers extra
features.
The official home site for nano, www.nano-editor.org, is a useful resource for nano users. Most
important for users of pre-10.4 releases of Mac OS X, which didnt include nano, the site provides
free downloads of the program. Here are instructions for installing it:
Download the source by clicking the link at the bottom of the screen labeled Get Nano, and then
clicking on the file nano-[versionnumber].tar.gz. The source builds right out of the box in
Panther. Type the following lines into the Terminal window (the version number may differ from
the one shown here).
$ tar xzf nano-1.2.5.tar.gz
$ cd nano-1.2.5/
$ ./configure
# ...much output here....
$ make
$ sudo make install
Password:
# ...output here...
The executable will be installed into /usr/local/bin/nano, and the man pages into
/usr/local/man/. If /usr/local/bin is not in your search path, you will either need to add it, or
else invoke nano using its full pathname.
Syntax Colour in Nano
Nano is capable of syntax highlighting. For example, we can tell it to display HTML tags such as
<head> in blue, and escaped characters such as & in red. Enter the following into the nanorc
file, called .nanorc in your home directory.
#HTML Syntax Highlighting
Unix_tricks.txt Page 254 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Tidy is a utility to syntax check and tidy HTML source code. Type:
$ tidy -h
to see a help screen.
See sourceforge. Tidy can be installed in earlier versions of Mac OS X from sourceforge and
presumably other ports like Fink and DarwinPorts.
If you're into the vim editor, tidy can be used to great effect to check and tidy HTML source
files directly within vim. Add the following file type plugin for html files.
$ cat ~/.vim/ftplugin/html/tidy.vim
""""""""""""""""""""""""""""""""""""""""""""""
" Adrian's vim filetype plugin for html tags "
""""""""""""""""""""""""""""""""""""""""""""""
"
" Maintainer: Adrian Mayo
" URL: http://www.mayo-family.com/vim/ftplugin/html/tidy.vim
" MAIN URL: http://www.mayo-family.com/vim
"
" This file defines maps to call the html tidy program, see:
" http://tidy.sourgeforge.net/
"
" This file should be placed in ~/.vim/ftplugin/html/ on Unix systems
" and will automatically be sourced for filetype = html.
"
" Map to check html and display error in a new window
"
noremap \err :1<CR>yG :20new<CR>p :setfiletype html<CR> :%!tidy -i >/dev/null<CR>
" Map to tidy html
"
noremap \tidy :%!tidy -i -f /dev/null<CR>
" END
"
The new commands are invoked by typing \err and \tidy in normal mode.
Redirection - Echo to Standard Error
They're of most use in shell scripts, but can be used directly on the command line too.
First off, here's how a script might send an error message to standard error instead of standard
out. Normally, when your script writes any type of message using a command such as 'echo', output
is sent to standard out.
Why would we want to write to standard error instead? Traditionally, Unix commands such as 'ls'
and 'mount' write output to standard out and error messages to standard error. This convention
ensures that if we were to pipe the command's output to another command (or redirect it to a
file) error messages would still displayed on the terminal screen and would not get mixed up with
the 'normal' output. We want our scripts to honour this convention.
As an example, let's write the error message "Can't open the file." to standard error.
$ cat example
Unix_tricks.txt Page 256 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
#!/bin/bash
# some processing here...
echo "Can't open the file." 1>&2
The key is this after the echo command:
1>&2
which merges standard out (stream 1) into standard error (stream 2), hence causing 'echo' to
write to standard error.
We run it and see the error message:
$ ./example
Can't open the file.
$
To prove the message went to standard error and not standard out, we redirect standard out to the
file 'out' and standard error to the file 'err', displaying the contents of each file:
$ ./example >out 2>err
$ cat out
$ cat err
Can't open the file.
$
Here's an example in which we issue a command and want to report an error if the command failed.
We test the special shell variable '$?': if it's zero then no error occurred, if its non-zero the
command failed. Naturally, we want to send our error message to standard error, not standard out.
Here's how we achieve this in an example where we test the error status returned from the 'ls'
command. To prove the message was sent to standard error and not standard out, we redirect
standard out to the file 'out', but still see the error message on the screen.
$ ls zzz
ls: zzz: No such file or directory
$ if (($?!=0)); then echo "An error occurred" 1>&2; fi > out
An error occurred
Notice the use of '1>&2' to merge standard out into standard error, as in Monday's tip. Notice
also that we took advantage of Bash's integer expressions in:
(($?!=0))
More conventionally we would have written:
$ if [ $? != 0 ]; then echo "An error occurred" 1>&2; fi >out
Redirection - Redirect and Pipe Both
Monday's tip showed how we can make our scripts write error messages to standard error instead of
standard out, just like 'real' Unix commands. Today, we'll look at how to negate this convention
by merging the standard error from a command back into its standard out.
For example, suppose we wish to send both the standard out and standard error of 'ls' to the same
file. We use the special redirection '&>'. First, we'll test an 'ls' command line that lists
'file' (which fails) and the directory 'test' that contains 'file':
$ ls test file
ls: file: No such file or directory
test:
file
$
Next, we'll redirect standard out to 'out':
Unix_tricks.txt Page 257 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
going to the screen. Only later is error redirected to the file, too late for out to be
redirected too. (I've always though that the two syntaxes work the opposite way around to what
seems natural.)
To throw away either or both of standard out and standard error, redirect to the special file
/dev/null. For example, if we like only good news:
$ ls test file 2>/dev/null
test:
file
$
Redirect to a Variable
It's often useful to capture the output of a command into a shell variable. For example, this
command returns the device identifier of the device mounted at '/Volumes/pw'
$ df | awk '/\/Volumes\/pw/ {print $1}'
/dev/disk3s2
$
We capture the output from this command into a shell variable called 'device' by typing:
$ device="$(df | awk '/\/Volumes\/pw/ {print $1}')"
$ echo $device
/dev/disk3s2
$
(We might use $device in a 'mount' command later in the script.)
However, only standard out is captured, as shown in this next example where we form an invalid
'awk' command.
$ device="$(df | awk '/\/Volumes\/pw {print $1}')"
awk: non-terminated regular expression \/Volumes\... at source line 1
context is
>>> <<<
$ echo $device
$
To capture standard error too, we simply merge it with standard out using the syntax '2>&1'.
$ device="$(df | awk '/\/Volumes\/pw {print $1}' 2>&1)"
$ echo $device
awk: non-terminated regular expression \/Volumes\... at source line 1 context is >>> <<<
$
Note that other shells such as 'sh' and 'tcsh' use a different syntax for command substitution -
`command` instead of $(command):
$ device="`df | awk '/\/Volumes\/pw/ {print $1}'`"
Mysql - Grab Mysql
This week's tips show you how to install and set up the MySQL Database server, and how to
integrate it with the Apache-PHP module. We'll look at installing the latest stable release of
MySQL - 5.0.
First, download the binary distribution from MySQL.
Unix_tricks.txt Page 259 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Choose the appropriate Mac OS X Binary - 10.3 (Power PC), 10.4 (Power PC), or 10.4 (x86). I've
tried both the standard and the 64 bit versions for 10.4 Power PC. However, although the 64 bit
version works, I could not compile PHP against that distribution. I've had no problems with the
non-64 bit version running on an iMac G5.
You have a second choice too, between a Mac-style Package installer and a Unix style Tar package
(found towards the bottom of the HTML page in the 'special' section). These instructions assume
10.4 Power PC Tar package.
We'll install MySQL in the standard location of /usr/local. Type the following commands:
To become the root user (give your admin password) type:
$ sudo -s
Password:
#
To install MySQL (the name of the Tar archive will be different for other distributions; also,
I've assumed the tar file was downloaded to ~/Desktop) type:
# cd /usr/local
# tar -xzf ~/Desktop/mysql-standard-5.0.18-osx10.4-powerpc.tar.gz
# ln -s mysql-standard-4.1.16-apple-darwin8.2.0-powerpc mysql
This uncompresses the archive and makes a symbolic link (not a copy) called mysql to the actual
installation.
Next, apply the correct owner and permissions to the install:
# cd mysql
# chown -R root:mysql .
It's a good idea to add the path of the MySQL binaries to your PATH variable so you don't have to
type the full path name to execute a MySQL command. (This assumes you are using the Bash shell.
Other shells vary.) The path is '/usr/local/mysql/bin'. A typical PATH variable would be set in
/etc/profile and look like this:
declare -x PATH="\
/usr/local/bin:/usr/bin:/bin:\
/usr/local/sbin:/usr/sbin:/sbin:\
/usr/X11R6/bin:\
/usr/local/mysql/bin:\
"
The statement is broken into six lines. It's ok to break it like this in the file, provided '\'
is added to the end of each broken line. The line pertinent to MySQL is '/usr/local/mysql/bin:\'.
Tuesday's tip will show you how to create the MySQL database and test the installation. The
database will be created in a more useful location that the default /usr/local/mysql/data.
Mysql - Create a Database
The database directory, by default, is created under the actual installation in
/usr/local/mysql/data. It's better to create it elsewhere, perhaps under /var where most server
generated data, such as incoming mail and log files, is stored.
To become the root user (give your admin password) type:
$ sudo -s
Password:
#
Unix_tricks.txt Page 260 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Create the database directory /var/mysql-data (you can place it at another location if you wish).
Type:
# mkdir /var/mysql-data
# chown mysql:mysql /var/mysql-data
# chmod 770 /var/mysql-data
Create the MySQL configuration file, /etc/my.cnf, which tells mysql where the database it held.
This is what it should contain:
# cat /etc/my.cnf
[mysqld]
datadir=/var/mysql-data
Next, create the necessary administrative databases by typing:
# /usr/local/mysql/scripts/mysql_install_db --user=mysql --datadir=/var/mysql-data
This should create all the necessary files with owners mysql:mysql.
Test the installation - start the server by typing:
# mysqld_safe --user=mysql &
[1] 19718
# Starting mysqld daemon with databases from /var/mysql-data
(If mysqld_safe cannot be found, see Monday's tip to add the MySQL executable directory to your
PATH. Once you have done so, start a new terminal session. Alternatively, type the full path name
to this and other MySQL commands - eg /usr/local/mysql/bin/mysqld_safe.)
Check that the three default databases are there
# mysqlshow
+--------------------+
| Databases |
+--------------------+
| information_schema |
| mysql |
| test |
+--------------------+
MOST IMPORTANT: enable the root password by typing:
# mysqladmin -u root password my-password
# mysqladmin -u root -h hostname.domain.com password my-password
Replace 'hostname.domain.com' with your host's name (eg 'mymac.local'). You might get away with
typing just the first line. Obviously, replace my-password with your chosen MySQL root password.
(Don't confuse the Unix root user with the MySQL root user - they are entirely separate.)
NOTE: From this point on, it's necessary to give the option -p to most MySQL commands and type
root's password at the prompt.
As a further test of successful installation, stop the server by typing:
# mysqladmin -u root -p shutdown
Enter password:
STOPPING server from pid file /var/mysql-data/...
060111 00:51:57 mysqld ended
[1]+ Done mysqld_safe --user=mysql
#
Next, we must create a Mac OS X startup item so MySQL will be launched when your Mac starts.
Unix_tricks.txt Page 261 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
NOTE: Run
./configure --help
for a full list of the configure options.
Restart Apache (if it's already running) so the new PHP module is read.
# apachectl graceful
Install extra packages with PEAR. The install does not include the DB package by default. (Why?)
It can be installed by user root using pear. Type:
# pear install DB
NOTE: Some handy pear commands:
pear # lists all pear commands
pear help command # get help on a specific command
pear config-show # shows the config settings, these
should point to directories
within /usr/local
pear list # list all installed packages
With any luck, you should have an Apache-PHP module that talks to the MySQL 5 installation from
the earlier tips.
Scripting one-liners - Authentication
Suppose you have a script that uses 'sudo' to execute a command as root. When the script executes
sudo, the user must authenticate with a password. If the user fails to authenticate then sudo
will not run the command and we must terminate the script.
If the script is already part-way through execution we might have to undo some of the stages its
already gone through. Ideally, we want to achieve authentication at the start of the scrip, even
though the command that requires it occurs later.
To do this we rely on the fact that a sudo authentication is valid for five minutes. Place this
line at the start of the script:
sudo -p Admin password echo 2> /dev/null || { echo Incorrect ; exit; }
How does it work? Option -p causes sudo prints a prompt. The sudo command just executes a null
echo command. If authentication is not successful, sudo returns failure (or False) and the
following statement (after the || 'OR') operator is executed. This statement prints an error
message and exits.
If authentication is successful, sudo returns success (or True) and the second statement is
skipped.
An OR operator executes its statements only in order to produce a Boolean result, True or False.
It reasons that if the first statement yields True, then the second statement needn't be executed
because it won't affect the overall result - True OR anything = True.
Similarly, the AND operator (&&) doesn't execute its second statement if the first yields False -
False AND anything = False.
Scripting One-Liners - Fancy Printing
Ever wanted to spice up the output from a script, like aligning numbers or zero padding them.
Then consider using 'printf' over echo.
Unix_tricks.txt Page 265 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
by Photoshop contains data that is the image itself, but the filename, date stamp, owners, file
size, and such like form the metadata.
(Note: Spotlight was introduced in Mac OS X Tiger 10.4)
Apple, being a nice Unix player, has opened up the power of Spotlight to the command line. Let's
look at what's available in Terminal land.
The 'mdls' command (metadata ls) shows all metadata attached to a file or directory. For example,
if we mdls a TextEdit file saved in Rich Text format we see:
$ mdls schedule.rtf
schedule.rtf -------------
kMDItemAttributeChangeDate = 2006-02-06 00:59:21 +0000
kMDItemContentCreationDate = 2004-06-03 17:17:17 +0100
kMDItemContentModificationDate = 2006-02-06 00:59:21 +0000
kMDItemContentType = "public.rtf"
kMDItemContentTypeTree = ("public.rtf", "public.text", "public.data", "public.item",
"public.content")
kMDItemDisplayName = "schedule"
kMDItemFSContentChangeDate = 2006-02-06 00:59:21 +0000
kMDItemFSCreationDate = 2004-06-03 17:17:17 +0100
kMDItemFSCreatorCode = 0
kMDItemFSFinderFlags = 16
kMDItemFSInvisible = 0
kMDItemFSIsExtensionHidden = 1
kMDItemFSLabel = 0
kMDItemFSName = "schedule.rtf"
kMDItemFSNodeCount = 0
kMDItemFSOwnerGroupID = 501
kMDItemFSOwnerUserID = 501
kMDItemFSSize = 7607
kMDItemFSTypeCode = 0
kMDItemID = 923169
kMDItemKind = "Rich Text Format (RTF) document"
kMDItemLastUsedDate = 2006-02-06 00:59:21 +0000
kMDItemUsedDates = (
2006-01-14 00:00:00 +0000,
2006-01-20 00:00:00 +0000,
2006-01-21 00:00:00 +0000,
2006-01-23 00:00:00 +0000,
2006-02-05 00:00:00 +0000,
2006-02-06 00:00:00 +0000
)
You'll see all the usual metadata normally displayed by 'ls -l', such as filename name:
kMDItemFSName = "schedule.rtf"
and user and group owner (shown as numeric IDs - the translation to textual names is performed by
ls):
kMDItemFSOwnerGroupID = 501
kMDItemFSOwnerUserID = 501
You'll notice other metadata too, such as:
kMDItemDisplayName - the name as shown in the Finder, which often means without the extension.
kMDItemKind - the type of content.
Unix_tricks.txt Page 268 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
Metadata with the types kMDItemFS* is information from the filesystem. Other metadata types
concern the file contents and is not information that ls reports.
BTW, Spotlight stores its content index and metadata store are held in a directory at the top
level of each file system:
$ sudo ls /.Spotlight-V100/
.journalHistoryLog ContentIndex.db _exclusions.plist store.db
.store.db _IndexPolicy.plist _rules.plist
Each file system has its own Spotlight store ensuring that this information travels with the
files to which it belongs.
Tomorrow, we'll look at what metadata is available for other types of file content.
Spotlight - Pictures and Songs
Today's tip looks at file content metadata. Different types of file content have different types
of metadata. Let's pass a photoshop file to mdls:
$ mdls heart.psd
heart.psd -------------
kMDItemAttributeChangeDate = 2006-01-03 00:21:09 +0000
kMDItemBitsPerSample = 40
kMDItemColorSpace = "RGB"
kMDItemContentCreationDate = 2004-02-06 02:12:09 +0000
kMDItemContentModificationDate = 2004-02-06 02:12:09 +0000
kMDItemContentType = "com.adobe.photoshop-image"
kMDItemContentTypeTree = (
"com.adobe.photoshop-image",
"public.image",
"public.data",
"public.item",
"public.content"
)
kMDItemCreator = "Adobe Photoshop 7.0"
kMDItemDisplayName = "heart"
kMDItemFSContentChangeDate = 2004-02-06 02:12:09 +0000
kMDItemFSCreationDate = 2004-02-06 02:12:09 +0000
kMDItemFSCreatorCode = 943868237
kMDItemFSFinderFlags = 1024
kMDItemFSInvisible = 0
kMDItemFSIsExtensionHidden = 1
kMDItemFSLabel = 0
kMDItemFSName = "jj1.psd"
kMDItemFSNodeCount = 0
kMDItemFSOwnerGroupID = 501
kMDItemFSOwnerUserID = 501
kMDItemFSSize = 2204679
kMDItemFSTypeCode = 943870035
kMDItemHasAlphaChannel = 1
kMDItemID = 923966
kMDItemKind = "Adobe Photoshop file"
kMDItemLastUsedDate = 2006-01-03 00:21:09 +0000
kMDItemLayerNames = (jan, hair)
kMDItemOrientation = 0
kMDItemPixelHeight = 718
kMDItemPixelWidth = 450
Unix_tricks.txt Page 269 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
kMDItemResolutionHeightDPI = 72.00006
kMDItemResolutionWidthDPI = 72.00006
kMDItemUsedDates = (2005-11-02 12:18:02 +0000, 2006-01-03 00:00:00 +0000)
The last few items show metadata describing the image size, resolution, and even on what
photoshop layers it contains.
Try passing a song file to mdls. The metadata includes the genre, title, and bit rate.
...
kMDItemMediaTypes = (Sound)
kMDItemMusicalGenre = "Alternative & Punk"
kMDItemStreamable = 0
kMDItemTitle = "Like It Or Leave It"
kMDItemTotalBitRate = 191384
...
To extract a specific metadata item, pass the item name as an argument to the name option:
$ mdls -name kMDItemAuthors "12 DARE.m4a"
12 DARE.m4a -------------
kMDItemAuthors = (Gorillaz)
$
At this point, you might be tempted to form a 'find/xargs/grep' command line to search through,
say, all music for songs a particular Genre. But Apple has provided a much better way of doing
this...a command line spotlight.
Tomorrow's tip will look at spotlighting on the command line.
A tip to save you life: "Remember to buy her a card!"
Spotlight - Spotlight from the Command Line
So far we've seen how to examine file metadata using the mdls command. To search the file system
for specific content or metadata, use mdfind. Here's an example to search for the text "DOCSIS
MIB" (it's a cable TV term):
$ mdfind "DOCSIS MIB"
/Users/saruman/1dot1/Miri/emulator/design/figure-1.psd
/Users/saruman/Sites/adrian/data-base/rfc-index.txt
/Users/saruman/1dot1/Miri/emulator/proposals/Emulator.swx
/Users/saruman/1dot1/Miri/emulator/design/Emulator.pdf
These results should match those found by pressing Command-Space and entering the same term into
the Spotlight bar.
Here's another example:
$ mdfind "miranda"
/Users/Shared/Books/Folders/Shakespear/COMEDY/TEMPEST.PDF
Or:
$ mdfind "kiss me, kate"
/Users/Shared/Books/Folders/Shakespear/COMEDY/LOVESLAB.PDF
/Users/Shared/Books/Folders/Shakespear/COMEDY/MEASURE.PDF
/Users/Shared/Books/Folders/Shakespear/COMEDY/SHREW.PDF
/Users/Shared/Books/Folders/Shakespear/COMEDY/TEMPEST.PDF
Why does this search produce more results than just SHREW.PDF? Because Spotlight is looking for
kiss AND me AND kate
Unix_tricks.txt Page 270 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
(it ignores the comma) but not necessarily consecutively. If all three words appear somewhere in
a document, that document will match the search.
We can also look for specific metadata. Tuesday's tip showed us that songs include the metadata
item
kMDItemMusicalGenre
Let's search for all songs that are 'Hip Hop/Rap':
$ mdfind "kMDItemMusicalGenre == 'Hip Hop/Rap'"
/Users/sharing/Music/iTunes/iTunes Music/Compilations/Encore/01 Curtains Up.m4a
/Users/sharing/Music/iTunes/iTunes Music/Compilations/Encore/02 Evil Deeds.m4a
/Users/sharing/Music/iTunes/iTunes Music/Compilations/Encore/03 Never Enough.m4a
/Users/sharing/Music/iTunes/iTunes Music/Compilations/Encore/04 Yellow Brick Road.m4a
/Users/sharing/Music/iTunes/iTunes Music/Compilations/Encore/05 Like Toy Soldiers.m4a
Use mdls to discover other types of metadata for other content types.
Tomorrow's tip will show how to perform more complex searches using mdfind.
Spotlight - Boolean Expressions
Yesterday, we saw how to use mdfind to find files that contain particular text, or have
particular metadata. Today we'll look at how we can be more exacting about what words a file
should, or should not, contain.
To illustrate, we have three simple text files:
$ cat file1.txt
Kiss me
$ cat file2.txt
Kate
$ cat file3.txt
Kiss me, kate
Let's look for files that contain the word "kiss":
$ mdfind -onlyin ~/splotlight-test "kiss"
/Users/saruman/splotlight-test/file1.txt
/Users/saruman/splotlight-test/file3.txt
Note use of the option -onlyin to limit searches to files rooted in a particular directory
hierarchy. You must specify the full path name of the directory.
And files that contain the word "kate":
$ mdfind -onlyin ~/splotlight-test "kate"
/Users/saruman/splotlight-test/file2.txt
/Users/saruman/splotlight-test/file3.txt
If we specify both terms, Spotlight will return those files that contain ALL the terms - in this
example "kiss" AND "kate".
$ mdfind -onlyin ~/splotlight-test "kiss kate"
/Users/saruman/splotlight-test/file3.txt
To test for "kiss" OR "kate", we separate the terms with '|'.
$ mdfind -onlyin ~/splotlight-test "kiss|kate"
/Users/saruman/splotlight-test/file1.txt
/Users/saruman/splotlight-test/file2.txt
/Users/saruman/splotlight-test/file3.txt
There must be no white space around the '|' symbol.
Unix_tricks.txt Page 271 of 271
Printed: Tuesday, May 27, 2008 3:20:00 PM
What about not kissing kate?
$ mdfind -onlyin ~/splotlight-test "kiss(-kate)"
/Users/saruman/splotlight-test/file1.txt
Again, make sure there is no white space between the terms.
And finally, "kiss" or "kate", but not "me".
$ mdfind -onlyin ~/splotlight-test "kiss|kate(-me)"
/Users/saruman/splotlight-test/file2.txt
Tomorrow, we'll combine mdfind with regular Unix commands such as find, grep, and xargs.
Spotlight - Piping its Output
Just like the regular Unix find, the list of files returned by mdfind can be piped to other Unix
commands for further processing.
The next command will filter the results from find leaving only files that have the extension
".pdf".
$ mdfind "kiss kate" | grep -i "\.PDF$"
We escaped the dot because it has a special meaning to regular expression, and used the dollar
symbol to anchor matching text to the end of the term (i.e. matching filenames must end in .pdf,
not just have .pdf in the middle).
To open all the files in Preview type:
$ mdfind -onlyin /Users/shared/books "kiss kate" | xargs open
To make the search term more selective, use grep and regular expressions. Here's a simple
example:
$ cat file1.txt
Kiss me $ cat file3.txt
Kiss me, kate $ mdfind -onlyin /Users/saruman/splotlight-test "kiss me,"
/Users/saruman/splotlight-test/file1.txt
/Users/saruman/splotlight-test/file3.txt
The comma is ignored by spotlight. Let's use grep:
$ mdfind -onlyin /Users/saruman/splotlight-test "kiss me," | xargs grep -i "Kiss me,"
/Users/saruman/splotlight-test/file3.txt:Kiss me, kate
grep: /Users/saruman/splotlight-test/file: No such file or directory
grep: 4.txt: No such file or directory
$
(Tip: Use grep to make the search term case sensitive.)
The command choked on the filename "file 4.txt" because it contains a space. Specify option -0
(zero) to both mdfind and xargs to fix this.
$ mdfind -0 -onlyin /Users/saruman/splotlight-test "kiss me," | xargs -0 grep -i "Kiss me,"
/Users/saruman/splotlight-test/file3.txt:Kiss me, kate
/Users/saruman/splotlight-test/file 4.txt:Kiss me, kate
$
This is the same trick as used with "find | xargs" as is described here.