Writing Aliases in csh and tcsh

Simple aliases

The csh and tcsh shells provide an alias command that allows you to make up new commands as abbreviations for longer, more complex commands. For example,

alias ll ls -l
defines a command named ll which lists files in long form (i.e. with protections, sizes, modification dates, etc.) You can use it by itself:
ll
total 20
-rw-r--r--    1 sbloch   users        4583 Oct 13 16:10 hw2.html
-rw-r--r--    1 sbloch   users        3273 Oct 13 16:46 hw3.html
-rw-r--r--    1 sbloch   users         540 Oct 11 13:49 index.html
drwx------    2 sbloch   users        4096 Oct 11 12:01 old
or with arguments:
ll ~/html/class/271/assignments/h*
-rw-r--r--    1 sbloch   users        4583 Oct 13 16:10 /users/staff/math/sbloch/html/class/271/assignments/hw2.html
-rw-r--r--    1 sbloch   users        3273 Oct 13 16:46 /users/staff/math/sbloch/html/class/271/assignments/hw3.html
Note that the command ll is simply replaced by ls -l in the command line, leaving the rest intact.

Using command-line arguments

Sometimes you want an alias to deal explicitly with its arguments, rather than just leaving them at the end of the command. For example, suppose you had a file in your home directory named friends:

Joe Schmoe	123 Main Street	212-987-6543
Jane Doe	975 First Ave	516-357-6420
Bob T. Slob	111 River Road	718-432-1098
Bill T. Pill	1 South Ave	516-877-0000
and you wanted to look up entries in this file easily:
% friend Joe
Joe Schmoe	123 Main Street	212-987-6543
% friend 516
Jane Doe	975 First Ave	516-357-6420
Bill T. Pill	1 South Ave	516-877-0000
A natural way to write this is using grep:
alias friend grep  ~/friends
doesn't work, because grep expects the string you're searching for to be the first argument, followed by the file you're searching. So we need to put the argument of friend just after the word grep, before the filename ~/friends.

As it happens, csh records the command you typed in its history before expanding aliases, so you can use the history substitution features to find out what the arguments were to the latest command (e.g. "friend Joe"). To review...

So let's try
alias friend grep !:1 ~/friends
Unfortunately, this doesn't work, because the shell recognizes "!:1" as a history reference before it executes the alias command; in other words, this takes the first argument of whatever command was executed just before the alias, and defines friend to always search for that word!

So we need a way to tell the shell not to try "!:1" as a history reference. As is so often the case in Unix, we do this with a backslash:

alias friend grep \!:1 ~/friends
And now the friend command works as desired.

For another (sillier) example, suppose we wanted a thrice command which took one argument and printed it three times:

% thrice bandersnatch
bandersnatch bandersnatch bandersnatch
Our first attempt might be
alias thrice echo !:1 !:1 !:1
but again, the "!:1" is recognized before the alias command even starts, rather than when the thrice command is used; a working version is
alias thrice echo \!:1 \!:1 \!:1

Quoting

Protecting a sequence semicolon

Suppose we wanted the thrice command to print its argument three times, each on a separate line. An easy way to do this is with three separate echo commands, separated by semicolons:

alias thrice echo \!:1 ; echo \!:1 ; echo \!:1
Unfortunately, the shell breaks the command line at semicolons before executing the alias command, so this has the effect of defining thrice to be simply "echo !:1", and then printing the string "!:1" twice on separate lines immediately. We want the semicolons to apply after thrice is expanded, rather than before it is defined. So we use single-quotes:
alias thrice 'echo \!:1 ; echo \!:1 ; echo \!:1'
which works. (In this case, double-quotes would also have worked. For reasons we'll see later, single-quotes are generally preferred for aliases.)

For another example, suppose we wanted a showstuff alias that prints out the current date, this month's calendar, the word "Files:", and a list of files in the current directory.

alias showstuff date ; cal ; echo Files: ; ls
doesn't work, because again the semicolons apply before alias starts, rather than after showstuff is expanded.
alias showstuff 'date ; cal ; echo Files: ; ls'
works much better.

Protecting a pipe

Suppose we want a command lnd which lists, with details, all the non-directory files in the current directory. We could try

alias lnd ls -l | grep -v ^d
(recall that in an "ls -l" listing, directories always have "d" as the first character on the line). But the shell recognizes the pipe character before the alias command starts, so it defines lnd to stand for "ls -l", and pipes the nonexistent output of the alias command into grep -v ^d, which is not what we wanted. In fact, for reasons I don't want to go into here, the alias doesn't seem to have any effect: the lnd command isn't recognized! Again, we'll fix the problem with single-quotes:
alias lnd 'ls -l | grep -v ^d'
which works.

Protecting I/O redirection

The same issue arises if an alias tries to do I/O redirection. Suppose we wanted a savels command to do an ls -l but put the results into a specified file. We might try

alias savels ls -l >\!:1
but the shell interprets the > character for output redirection before alias starts, rather than after savels is expanded. So instead we say
alias savels 'ls -l >\!:1'

If you try this savels command and look at the resulting file, you'll notice that the file contains its own name. If we want only the other files in the directory, we can fix it:

alias savels 'ls -l | grep -v \!:1 >\!:1'

Protecting globbing wildcards

Let's go back to thrice, but suppose we wanted it to take arbitrarily many arguments and repeat them all three times:

% thrice this is a test
this is a test this is a test this is a test
We recall that !* stands for all the arguments of the latest command, so we try
alias thrice echo \!* \!* \!*
but we immediately get an error message, before we can even try thrice. The explanation is that the character * also means "all the filenames in the current directory", and filename globbing (expansion of *, ?, [a-z], and similar patterns) happens before alias executes, rather than after thrice is expanded. Once again, single quotes to the rescue:
alias thrice 'echo \!* \!* \!*'

Single vs. double quotes: protecting variable expansion

All of the examples so far would have also worked using double quotes instead of single quotes. But let's try indir, which treats its first parameter as a directory to move to, then treats the rest of the parameters as a command to execute in that directory. For example,

indir ~sbloch/html/class/271 ls -l *html
should cd to the
~sbloch/html/class/271
directory, list all the HTML files in that directory, and cd back to where we were before. How do we write this?
alias indir 'set current=`pwd` ; cd \!:1 ; \!:2* ; cd $current'

Now, suppose we wrote the same thing using double quotes:

alias indir "set current=`pwd` ; cd \!:1 ; \!:2* ; cd $current"
Since the expansion of shell and environment variables happens after the recognition of single quotes, but before the recognition of double quotes, the shell would expand the variable name "current" at the time indir is defined, rather than while it's executing. If "current" doesn't happen to be defined when you type the alias indir ... command, you'll get an error message; if it is defined, indir will work oddly:
panther% pwd
/users/staff/math/sbloch/public_html/class/archive/271/fall2005/notes
panther% indir ~sbloch/html/class/271 ls -l *html
-rw-r--r--    1 sbloch   users        5134 Oct 14 10:52 271_calendar.shtml
-rw-r--r--    1 sbloch   users        6753 Oct 15 19:43 index.html
-rw-r--r--    1 sbloch   users        6373 Apr 10  2002 old_index.html
-rw-r--r--    1 sbloch   users        8852 Sep 20 12:01 syllabus.html
panther% pwd
/users/staff/math/sbloch
because "current" happened to be /users/staff/math/sbloch at the time I defined the alias.

On the other hand, if you actually want to use the value of a variable at the time the alias is defined rather than at the time it's used (uncommon but possible), then double quotes are the way to go.

Moral of the story

In general, unless you have a good reason not to, always put the body of an alias in single-quotes.

alias commandname 'blah blah blah blah blah'

Writing long aliases

If you're writing an alias for a multi-stage pipe, or just involving a lot of words, it can easily get too long to fit on one line comfortably. If you try to type

alias longcmd 'echo this is a very very very very very very very very
very very long alias command'
you won't get to the second line, because the shell will complain that the first line has mismatched quotation marks. So we use a backslash to tell the shell that the newline isn't actually the end of the command:
alias longcmd 'echo this is a very very very very very very very very \
very very long alias command'
This time there's no error message until you try longcmd, at which point it prints out
this is a very very very very very very very very
csh: very: Command not found.
The backslash prevented the shell from ending the alias command, but then longcmd expanded into something with a newline in the middle, and the shell at that time decided the newline meant the end of a command. So we fix it with another backslash:
alias longcmd 'echo this is a very very very very very very very very \\
very very long alias command'
Now one of the backslashes prevents the shell from ending the alias command, and the other is still left to prevent the shell from ending the echo command when longcmd is expanded.

Whether you entirely understood that or not, the rule of thumb is that if an alias definition is more than one line long, put a double backslash at the end of each line except the last.


Last modified: Sun Oct 16 08:07:10 EDT 2005
Stephen Bloch / sbloch@adelphi.edu