Which Shell Is Which?

Let's dig in and find out some of the key additional capabilities of the most popular UNIX user shells.

Task 14.1: The C Shell and Korn Shell History Mechanisms

If you went through school in the United States, you doubtless have heard the aphorism “Those who do not study history are doomed to repeat it.” UNIX stands this concept on its head. For UNIX, the aphorism is best stated “Those who are aware of their history can easily repeat it.”


Both the C shell and the Korn shell build a table of commands as you enter them and assign them each a command number. Each time you log in, the first command you enter is command 1, and the command number is incremented for each subsequent command you enter. You can review or repeat any previous command easily with just a few keystrokes.

To review your history in the C shell, enter history at the csh prompt. Odds are that nothing will happen, though, because by default csh remembers only the very last command. To have it begin building a list of commands, you must turn on this feature through the environment setting set history=n, where n is the number of commands you'd like it to recall.

By contrast, ksh has a default history list size of 128 commands, plenty for anyone. To review your history in ksh, you also can use the history command. Actually, though, it's an alias, and the real command is the more cryptic fc -l. In Korn shell, you don't need to make any changes; the history mechanism is ready to use immediately.

  1. Log in to your system so that you have a C shell prompt. If you're currently in the Bourne shell or a shell other than C shell, this would be a great time to use chsh to change shells.

    % history
    
    %
    

    The shell indicates that it has no history. Sir Winston Churchill doubtless would shake his head and mutter under his breath, “To have become such a sophisticated operating system yet never to have studied history!”

  2. I need to turn on the shell history mechanism, so I will enter the following command:

    % set history=100
    							

    Still there is no feedback, but I can check the status of all the shell parameters by entering set at the csh prompt:

    % set
    argv    ()
    cwd     /users/taylor
    filec
    history 100
    home    /users/taylor
    host    limbo
    noclobber
    path    (. /users/taylor/bin /bin /usr/bin /usr/ucb /usr/local /etc
    /usr/etc /usr/local/bin /usr/unsup/bin
    savehist        50
    shell   /bin/csh
    status  0
    system  limbo
    term    unknown
    user    taylor
    
  3. To take a break, I use w to see who is logged on and what they're doing, use ls to check my files again, and use date to see whether my watch is working:

    % w | head
     11:41am  up 17:59,  103 users,  load average: 0.54, 0.53, 0.49
    User     tty       login@  idle   JCPU   PCPU  what
    root     console   6:02pm 12:13      1      1  -csh
    taylor   ttyAf    11:40am            5      2  w
    bev      ttyAg     9:25am    14   1:09      3  -csh
    rekunkel ttyAh    11:37am            4      3  rlogin ccn
    gabh     ttyAi    10:41am     6     46     16  talk dorits
    af5      ttyAj     8:27am    21     33      1  -ksh
    techman  ttyAk     9:47am           25      7  gopher
    tuccie   ttyAl    11:37am            1      1  mail
    Broken pipe
    % ls
    Archives/          OWL/               buckaroo.confused  sample
    InfoWorld/         awkscript          dickens.note       sample2
    Mail/              bin/               keylime.pie        src/
    News/              buckaroo           owl.c              temp/
    % date
    Tue Dec  8 11:41:29 EST 1998
    

Notice that at the end of the w command output, the system noted Broken pipe. This is nothing to be anxious about; it's just an indication that there was much more in the pipeline when the program quit. You can see that head read only the first 10 lines. The first line of the w output shows that there are 103 users on the system, which means that head ignored 94 lines of output. Unlike real plumbing, fortunately, this broken pipe doesn't allow the spare data to spill onto the basement floor!


  1. Now, when I enter history, the shell remembers the previous commands, presenting them all in a neat, numbered list:

    % history
        1  set history=100
        2  w | head
        3  ls
        4  date
        5  history
    
  2. To turn this on permanently, add the set history command to your .cshrc. If you want the shell to remember commands even if you log out and log back in, also specify the setting savehist. I choose to do this by entering vi +$ .cshrc and adding the following line:

      set noclobber system=limbo filec
      umask 007
    
      setprompt
    endif
    set history=100 savehist=50 _
    ~
    ~
    

    If you glance back at the output of the set command, you can see that I already have both of these parameters set: 100 commands will remain in the history list while I'm working, and 50 commands will be retained for the next time I log in. What's particularly helpful is that any time I specify a number n for either history list, the shell actually saves the most recent n commands, so I have the most recent 100 commands for review while I'm using the system, and the 50 most recent commands remembered when I log in later.

    Make this change to your .cshrc file, log out, and log in again to ensure that your history mechanism is set up correctly.

As with much of UNIX, turning on the history mechanism of the C shell is quite easy once you learn the trick. In this case, simply remember that you need to specify a set history value to have the shell begin remembering what's going on with your interaction. In Korn shell, you don't need to make any changes; it's ready to use immediately.


Task 14.2: Using History to Cut Down on Typing

There are three main mechanisms for working with the history list. You can specify a previous command by its command number, by the first word of the command, or, if you're working with the most recently executed command, by a special notation that easily fixes any mistakes you might have made as you typed it.


Every history command begins with an exclamation point. If the 33rd command you entered was the who command, for example, you can execute it by referring to its command number: Enter !33 at the command line. You can execute it also by entering one or more characters of the command: !w, !wh, or !who. You must enter enough characters to uniquely identify it in the history list.

To edit a previous command, type a caret, the pattern you want to change, another caret, and the correct pattern. If you just entered awk -F, '{print $2}' and realize that you meant to type a colon, not a comma, as the field delimiter, ^,^: will do the trick.

A very useful shorthand is !!, which repeats the most recently executed command. Two other history references are valuable to know: !$ expands to the last word of the preceding line (which makes sense because $ always refers to the end of something, whether it be a line, the file, or, in this case, a command), and !* expands to all the words in the preceding command except the first word. So, for example, if I entered the command ls /usr /etc /dev and then immediately entered the command echo !*, the second command would be expanded automatically to echo /usr /etc /dev.

Korn shell offers all of this and more. You can repeat commands by number by specifying rn, where n is the command number (for example, r33). You can also repeat by name with rname, as in rwho to repeat the most recent who command. Much more useful is the ksh capability to directly edit a command with the familiar vi or emacs command keys, without leaving the command line. Without any arguments, r will repeat the preceding command.

  1. First, I need to spend a few minutes building up a history list by running various commands:

    % w | head
     11:58am  up 18:14,  81 users,  load average: 0.54, 0.44, 0.38
    User     tty       login@  idle   JCPU   PCPU  what
    root     console   6:02pm 12:30      1      1  -csh
    hopkins  ttyAe    11:49am            4      4  telnet whip.isca.uiowa.edu
    taylor   ttyAf    11:40am            8      2  wbev      ttyAg 9:25am 31   1:09      3  -csh
    af5      ttyAj     8:27am    37     33      1  -ksh
    techman  ttyAk     9:47am     4   1:11      4  elm
    tuccie   ttyAl    11:37am            2      1  mail
    trice    ttyAm     8:16am  1:21      5      2  -csh
    Broken pipe
    % date
    Tue Dec  8 11:58:19 EST 1998
    % ls
    Archives/          OWL/               buckaroo.confused  sample
    InfoWorld/         awkscript          dickens.note       sample2
    Mail/              bin/               keylime.pie        src/
    News/              buckaroo           owl.c              temp/
    % cat buckaroo
    I found myself stealing a peek at my own watch and overhead
    General Catbird's
    aide give him the latest.
    "He's not even here," went the conversation.
    "Who?"
    "Banzai."
    "Where the hell is he?"
    "At the hospital in El Paso."
    "What? Why weren't we informed? What's wrong with him?"
    %
    
  2. Now I will check my history list to see what commands are squirreled away for later:

    % history
        51  set history=100
        52  history
        53  w | head
        54  date
        55  ls
        56  cat buckaroo
        57  history
    

I already have my history mechanism turned on, so my commands begin numbering with 51 rather than with 1. Your system might be different. Regardless of what the command numbers are, they'll work!


  1. To repeat the date command, I can specify its command number:

    % !54
    date
    Tue Dec  8 12:04:08 EST 1998
    

    Notice that the shell shows the command I've entered as command number 54. The ksh equivalent here would be r 54.

  2. A second way to accomplish this repeat, a way that is much easier, is to specify the first letter of the command:

    % !w
    w | head
     12:05pm  up 18:23,  87 users,  load average: 0.40, 0.39, 0.33
    User     tty       login@  idle   JCPU   PCPU  what
    root     console   6:02pm 12:37      1      1  -csh
    lloyds   ttyAb    12:05pm            1      1  mail windberg
    lusk     ttyAc    12:03pm            3      2  gopher
    hopkins  ttyAe    11:49am            8      8  telnet whip.isca.uiowa.edu
    taylor   ttyAf    11:40am     1     14      3  w
    bev      ttyAg     9:25am    38   1:09      3  -csh
    libphar  ttyAh    12:03pm            3      3  elm
    dgrove   ttyAi    12:02pm            5      2  more inbox/16
    Broken pipe
    
  3. Now glance at the history list:

    % history
        51  set history=100
        52  history
        53  w | head
        54  date
        55  ls
        56  cat buckaroo
        57  history
        58  date
        59  w | head
        60  history
    

    Commands expanded by the history mechanism are stored as the expanded command, not as the history command that actually was entered. Thus, this is an exception to the earlier rule that the history mechanism always shows what was previously entered. It's an eminently helpful exception!

    History commands are quite helpful for people working on a software program. The most common cycle for programmers to repeat is edit-compile-run, over and over again. The commands UNIX programmers use most often probably will look something like vi test.c, cc -o test test.c, and test, to edit, compile, and run the program, respectively. Using the C shell history mechanism, a programmer easily can enter !v to edit the file, !c to compile it, and then !t to test it. As your commands become longer and more complex, this function proves more and more helpful.

  4. It's time to experiment a bit with file wildcards:

    % ls
    Archives           awkscript          dickens.note       src
    InfoWorld          bin                keylime.pie        temp
    Mail               buckaroo           owl.c
    News               buckaroo.confused  sample
    OWL                cshrc              sample2x
    

    Oops! I meant to specify the -F flag to ls. I can use !! to repeat the command; then I can add the flag:

    % !! -F
    ls -F
    Archives/          awkscript          dickens.note       src/
    InfoWorld/         bin/               keylime.pie        temp/
    Mail/              buckaroo           owl.c
    News/              buckaroo.confused  sample
    OWL/               cshrc              sample2
    

    I want to figure out a pattern or two that will let me specify both buckaroo files, the dickens file, and sample2, but not sample. This is a fine example of where the echo command can be helpful:

    % echo b* d* s*
    bin buckaroo buckaroo.confused dickens.note sample sample2 src
    

    That's not quite it. I'll try again:

    % echo bu* d* sa*
    buckaroo buckaroo.confused dickens.note sample sample2
    

    That's closer. Now I just need to remove the sample file:

    % echo bu* d* sa*2
    buckaroo buckaroo.confused dickens.note sample2
    

    That's it. Now I want to compute the number of lines in each of these files. If I use the csh history mechanism, I can avoid having to enter the filenames again:

    % wc -l !*
    wc -l bu* d* sa*2
          36 buckaroo
          11 buckaroo.confused
          28 dickens.note
           4 sample2
          79 total
    

    Notice that the !* expanded to the entire preceding command except the very first word.

The general idea of all these history mechanisms is that you specify a pattern that is replaced by the appropriate command in the history list. So you could enter echo !! to have the system echo the last command, and it would end up echoing twice. Try it.


Korn shell users will find that echo !! produces !! and that the ksh repeat-last-command of r also will fail. If your last command was echo r, the result will be r. Alas, there is no analogous shorthand to the convenient !! -F in csh. On the other hand, if FCEDIT is set to vi or EMACS, you can pop into the editor to change the command by typing fc.


  1. What happens if I use !$ instead?

    % wc -l !$
    wc -l sa*2
           4 sample2
    
  2. In the middle of doing all this, I became curious about how many people on my system have first names that are four letters long. Is this impossible to compute? Not with UNIX!

    The first step is to extract the full names from the /etc/passwd file:

    % awk -F: '{ print $5 }'
    							

    The system does not respond. I forgot to specify the filename!

    % !! < /etc/passwd
    awk -F: '{print $5}' < /etc/passwd
    limbo root,,,,
    USENET News,,,,
    INGRES Manager,,,,
    (1000 user system) DO NOT,,,,
    Vanilla Account,,,,
    The Ferryman,,,,
    

    I can use ^c to stop this output, because I've seen enough to know that it's what I want. Next, I use awk again to pull just the first names out of this list:

    % !! | awk '{print $1}'
    awk -F: '{print $5}' < /etc/passwd | awk '{print $1}'
    root
    USENET
    INGRES
    (1000
    Vanilla
    The
    Account
    ^c
    %
    

    It looks okay. Now the final step: I need to revise this awk script to look at the length of each name and output the name only if it's four letters long:

    % !-2 | awk '{ if (lng($1) == 4) print $0 }'
    awk -F: '{print $5} ' < /etc/passwd | awk '{ if (lng($1) == 4) print $0 }'
    

    I got no output at all! The reason is that I mistyped length as lng. Fortunately, to fix this is simplicity itself with C shell history commands. Remember, the format is ^old^new:

    % ^lng^length
    awk -F: '{print $5}' < /etc/passwd | awk '{ if (length($1) == 4) print $0 }'
    ,,,,
    Paul Town,,,,
    Pete Cheese,,,,
    John Smith,,,,
    Dana Tott,,,,
    Dick Ply,,,,
    Mike Moliak,,,,
    Bill Born,,,,
    Dale Tott,,,,
    Bill Rison,,,,
    Gary Flint,,,,
    Doug Sherwood,,,,
    Ruth Raffy,,,,
    Dave Sean,,,,
    ^c
    %
    

    That's very close. I just need to pipe the output of this command to wc:

    % !! | wc -l
    awk -F: '{print $5} ' < /etc/passwd | awk '{ if (length($1) == 4) print
    $0 }' | wc -l
         723
    
  3. If you are using Korn shell, here's where it shines! Make sure that the environment variable EDITOR is set to your preferred editor:

    $ echo $EDITOR
    vi
    $	
    

    Now, any time you're entering a command, you can press the Escape key and be in ksh history-edit command mode. The usual vi commands work, including h and l to move left and right; i and Escape to enter and leave insert mode; w, W, b, and B to zip about by words; and 0 and $ to move to the beginning or end of the line.

    Much more useful are k and j, which replace the current command with the preceding or next, enabling you to zip through the history list.

    If I'd just entered who and then ls, to append | wc -l to the who command, I could press the Escape key:

    $_.
    

    Now each time I type k, I will see the preceding command. Typing k once reveals this:

    $ls
    

    Typing k a second time reveals this:

    $who
    

    That's the right command, so $ moves the cursor to the end of the line:

    $who
    							

    Typing a appends, at which point I can add | wc -l like this:

    $who | wc -l
    							

    Pressing Return results in ksh actually executing the command:

    $ who | wc -l
        130
    $_
    

The history mechanisms of the shells are wonderful time savers when you're working with files. I find myself using the csh !! and !word mechanisms daily either to build up complex commands (such as the preceding example, in which I built up a very complex command, step by step) or to repeat the most recently used edit commands. Table 14.1 summarizes the available csh history mechanisms. I encourage you to learn and use them. They soon will become second nature and will save you lots of typing.


Table 14.1. C Shell History Commands
Command Function
!! Repeat the preceding command.
!$ Repeat the last word of the preceding command.
!* Repeat all but the first word of the preceding command.
^a^b Replace a with b in the preceding command.
!n Repeat command n from the history list.

Task 14.3: Command Aliases

If you think the history mechanism has the potential to save you typing, you'll be glad to learn about the command-alias mechanism in the Korn and C shells. Using aliases, you can easily define new commands that do whatever you'd like, or even redefine existing commands to work differently, have different default flags, or more!


The general format for using the alias mechanism in csh is alias word command-sequence, and in ksh it is alias word=commands. If you enter alias without any specified words, the output shows a list of aliases you have defined. If you enter alias word in csh, the output lists the current alias, if there is one, for the specified word.

  1. One of the most helpful aliases you can create specifies certain flags to ls so that each time you enter ls, the output will look as though you used the flags with the command. I like to have the -FC flags set.

    % ls
    Archives           awkscript          dickens.note       src
    InfoWorld          bin                keylime.pie        temp
    Mail               buckaroo           owl.c
    News               buckaroo.confused  sample
    OWL                cshrc              sample2
    

    Now I'll create a C shell alias and try it again:

    % alias ls 'ls -CF'
    % ls
    Archives/          awkscript          dickens.note       src/
    InfoWorld/         bin/               keylime.pie        temp/
    Mail/              buckaroo           owl.c
    News/              buckaroo.confused  sample
    OWL/               cshrc              sample2
    

    This is very helpful!

    The ksh equivalent would be alias ls = 'ls -CF'.

  2. If you're coming from the DOS world, you might have found some of the UNIX file commands confusing. In DOS, for example, you use DIR to list directories, REN to rename files, COPY to copy them, and so on. With aliases, you can re-create all those commands, mapping them to specific UNIX equivalents:

    % alias DIR 'ls -lF'
    % alias REN 'mv'
    % alias COPY 'cp -i'
    % alias DEL 'rm -i'
    % DIR
    total 33
    drwx------  2 taylor        512 Nov 21 10:39 Archives/
    drwx------  3 taylor        512 Dec  3 02:03 InfoWorld/
    drwx------  2 taylor       1024 Dec  3 01:43 Mail/
    drwx------  2 taylor        512 Oct  6 09:36 News/
    drwx------  4 taylor        532 Dec  6 18:31 OWL/
    -rw-rw----  1 taylor        126 Dec  3 16:34 awkscript
    drwx------  2 taylor        512 Oct 13 10:45 bin/
    -rw-rw----  1 taylor       1393 Dec  5 18:48 buckaroo
    -rw-rw----  1 taylor        458 Dec  4 23:22 buckaroo.confused
    -rw-------  1 taylor       1339 Dec  2 10:30 cshrc
    -rw-rw----  1 taylor       1123 Dec  5 18:16 dickens.note
    -rw-rw----  1 taylor      12556 Nov 16 09:49 keylime.pie
    -rw-rw----  1 taylor       8729 Dec  2 21:19 owl.c
    -rw-rw----  1 taylor        199 Dec  3 16:11 sample
    -rw-rw----  1 taylor        207 Dec  3 16:11 sample2
    drwx------  2 taylor        512 Oct 13 10:45 src/
    drwxrwx---  2 taylor        512 Nov  8 22:20 temp/
    % COPY sample newsample
    %
    
  3. To see what aliases have been defined, use the alias command:

    % alias
    COPY    cp -i
    DEL     rm -i
    DIR     ls -lF
    REN     mv
    ls      ls -CF
    
  4. You could improve the alias for DIR by having the output of ls fed directly into the more program so that a directory listing with a lot of output will automatically pause at the end of each page. To redefine an alias, just define it again:

    % alias DIR 'ls -lF | more'
    							

    To confirm that the alias is set as you desire, try this:

    % alias DIR
    DIR     ls -lF | more
    

If you're defining just one command with an alias, you don't really need to use the quotation marks around the command argument. But what would happen if you entered alias DIR ls -lF | more? The alias would be set to ls -lF, and the output of the alias command would be fed to the more program, which is quite different from what you desired. Therefore, it's just good form to use the quotation marks and a good habit to get into.


Aliases are a great addition to any command shell, and with the arcane UNIX commands, they also can be used to define full-word commands as synonyms. For example, if you decide you'd like the simplicity of remembering only the command move to move a file somewhere else, you could add the new alias alias move mv to your .cshrc file if you're using C shell or alias move=mv to your .profile if you prefer Korn shell, and the shell would include a new command.


Task 14.4: Some Power Aliases

Because I have used the C shell for many years, I have created various aliases to help me work efficiently. A few of the best are shown in this section.


  1. To see what aliases I have defined, I can use the same command I used earlier:

    % alias
    cd      chdir !* ; setprompt
    diff    /usr/bin/diff -c -w
    env     printenv
    from    frm -n
    info    ssinfo
    ls      /bin/ls -F
    mail    Mail
    mailq   /usr/lib/sendmail -bp
    netcom  echo Netcom login: taylor;rlogin netcom.com
    newaliases      echo you mean newalias...
    rd      readmsg $ | page
    rn      /usr/local/bin/rn -d$HOME -L -M -m -e -S -/
    setprompt       set prompt="$system ($cwd:t) ! : "
    ssinfo  echo "connecting..." ; rlogin oasis
    sunworld        echo SunWorld login: taylor;rlogin sunworld.com
    

    Recall that each of these aliases started out in my .cshrc file surrounded by single quotation marks:

    % grep alias .cshrc
    alias  diff     '/usr/bin/diff -c -w'
    alias  from     'frm -n'
    alias  ll       'ls -l'
    alias  ls       '/bin/ls -F'
    alias  mail     Mail
    alias  mailq    '/usr/lib/sendmail -bp'
    alias  netcom   'echo Netcom login: taylor;rlogin netcom.com'
    alias  sunworld 'echo SunWorld login: taylor;rlogin sunworld.com'
    alias  newaliases 'echo you mean newalias...'
    alias  rd       'readmsg $ | page'
    alias  rn       '/usr/local/bin/rn -d$HOME -L -M -m -e -S -/'
    alias  cd             'chdir !* ; setprompt'
    alias  env            'printenv'
    alias  setprompt      'set prompt="$system ($cwd:t) ! : "'
    # special aliases:
    alias info      ssinfo
    alias ssinfo    'echo "connecting..." ; rlogin oasis'
    

    Also notice that the shell always keeps an alphabetically sorted list of aliases, regardless of the order in which they were defined.

  2. Most of these aliases are easy to understand. For example, the first alias, diff, ensures that the command diff always has the default flags -c and -w. If I enter from, I want the system to invoke frm -n; if I enter ll, I want the system to invoke ls -l; and so on.

    Some commands can cause trouble if entered, so creating an alias for each of those commands is a good way to stay out of trouble. For example, I have an alias for newaliases; if I accidentally enter that command, the system gently reminds me that I probably meant to use the newalias command:

    % newaliases
    you mean newalias...
    
  3. I have created aliases for connecting to accounts on other systems. I like to name each alias after the system to which I'm connecting (for example, netcom, sunworld):

    % alias netcom
    echo Netcom login: taylor;rlogin netcom.com
    % alias sunworld
    echo SunWorld login: taylor;rlogin sunworld.com
    

    Separating commands with a semicolon is the UNIX way of having multiple commands on a single line, so when I enter the alias netcom, for example, it's as if I'd entered all these commands one after another:

    echo Netcom login: taylor
    rlogin netcom.com
    

You can't enter alias netcom sunworld to list the netcom and sunworld aliases because that command means to replace the alias for netcom with the command sunworld.


  1. Two aliases worth examining more closely are those for the cd and setprompt commands. As you learn in a few moments, you can set your shell prompt to be just about any characters you'd like. (Hang on for just a paragraph or two, and you will learn all about what's occurring in the next example!) I like to have my prompt indicate where in the file system I'm currently working. To ensure that the prompt is always up-to-date, I simply alias the cd command so that each time I change directories, the prompt is recalculated:

    % alias cd
    chdir !* ; setprompt
    % alias setprompt
    set prompt="$system ($cwd:t) ! : "
    

The chdir command does the same thing as cd and is intended for use within aliases as shown. So if you find chdir easier to remember than cd, you can use it instead.


Aliases are what makes both the C shell and Korn shell such great command interfaces. I can, and do, easily customize the set of commands and the default flags (look at all the options I set as default values for the rn command). I even turn off some commands that I don't want to enter accidentally. Let your imagination run wild with aliases. If you decide you really like one and you're using csh, add the alias to your .cshrc so that it's permanent (.profile if you're using ksh). If you want to turn off an alias, you can use the unalias command, and it's gone until you log in again. For example, unalias netcom would temporarily remove from the shell the netcom alias shown earlier.


Task 14.5: Setting Custom Prompts

Up to this point, the command prompt I've seen is a boring %. It turns out that the C shell lets you set your prompt to just about any possible value, with set prompt="value".


The Korn shell equivalent is even easier: PS1="value". Note that PS1 must be all uppercase for this to work.

  1. I'm getting tired of UNIX being so inhospitable. Fortunately, I easily can change how it responds to me:

    % set prompt="Yes, master? " 
    Yes, master?
    

    That's more fun!

    The ksh equivalent is PS1="Yes, master? "

  2. There are a lot of things you can tuck away in your prompt that can be of great help. The first useful variable is cwd, which holds the current working directory:

    Yes, master? set prompt="In $cwd, oh master: "
    In /users/taylor, oh master:
    

    What happens if I change directories?

    In /users/taylor, oh master: cd /
    In /users/taylor, oh master: pwd
    /
    In /users/taylor, oh master:
    

    This is not so good. Now you can see why it's necessary to alias cd to maintain the prompt.

  3. Some special ! values can be added to the prompt definition, as shown in Table 14.2.

    Here are a few examples of other C shell prompts and what happens when you use them:

    In /, oh master: set prompt="(!) % " 
    (132) %
    

    The ksh equivalent is PS1="(!) $ ".

    The number in parentheses is the command number, as used by the C shell history mechanism:

    (132) % echo hi
    hi
    (133) % ls News
    mailing.lists.usenet  usenet.1              usenet.alt
    (134) % !132
    echo hi
    hi
    (135) %
    

    Every time I log in, I automatically set the variable system to the name of the current computer:

    (135) % set prompt="$system (!) % " 
    limbo (136) %
    

    I like to include in my prompt the basename of the current directory, as shown in the following example. Basename means the closest directory name, so the basename of /usr/home/taylor is taylor, for example. Also, I replace the percent sign with a colon, which is a bit easier to read. There is a slight problem, however; having : instead of % means that I have to remember that I'm in C shell (or Korn shell, as the case may be).

    limbo (136) % set prompt="$system ($cwd:t) ! : " 
    limbo (taylor) 137 :
    
Table 14.2. Special Values for the System Prompt
Value Expands to
`cmd` The results of executing cmd.
! The current command number.
$var The value of var.
$var:t The tail (last word) of the value of var.

  1. Now I glance back at the aliases for setprompt and cd, with all these things in mind:

    limbo (taylor) 139 : alias cd
    chdir !* ; setprompt
    limbo (taylor) 140 : alias setprompt
    set prompt="$system ($cwd:t) ! : "
    limbo (taylor) 141 :
    

    You can see that the setprompt alias defines the C shell prompt as $system ($cwd:t) ! : ", although the actual line in the .cshrc file includes the backslash (as expected):

    limbo (taylor) 141 : grep prompt= .cshrc
      alias  setprompt      'set prompt="$system ($cwd:t) ! : "'
    limbo (taylor) 142 :
    

    Each time I change directories, I use the combined commands of the cd alias (chdir !*) to change the current directory, and then I use setprompt to compute the new prompt.

The !* notation in a shell alias expands to all the words you specify after the alias word on the command line. For example, if you have an alias for dir that is "echo !* ; ls !*", entering dir /home actually executes echo /home followed by ls /home.


Experiment and find a set of variables that can help you customize your UNIX prompt. I strongly recommend that you use command numbers to familiarize yourself with the history mechanism.


Task 14.6: Creating Simple Shell Scripts

The command-alias capability is a helpful way to cut down on entering short commands time and again, but what if you have a series of 5 or 10 commands you often enter in sequence? That's where shell scripts can help. At their simplest, shell scripts are a series of shell commands that appear in a file in exactly the order in which they'll be entered. If you change the permissions of the file to add execute permission, you can enter the name of the file as if it were just another UNIX command.


  1. Let's move into my own bin directory and consider a script or two I have there:

    limbo (ucb) 42 : cd
    limbo (taylor) 43 : cd bin
    limbo (bin) 44 : file * 
    bounce.msg:     executable shell script
    calc:   SYMMETRY i386 executable (0 @ 0) not stripped version 1
    fixit:  SYMMETRY i386 executable (0 @ 0) not stripped version 1
    massage:        SYMMETRY i386 executable (0 @ 0) not stripped version 1
    punt:   shell script
    rumor.mill.sh:  shell script
    say.hi: ascii text
    limbo (bin) 45 : cat -n punt
         1  : Use /bin/sh
         2
         3  # Punt: punt a news article from within "rn" to yourself.
         4
         5  trap "/bin/rm -f /tmp/punt.$$" 0 1 9 15
         6
         7  [email protected]
         8
         9  cat - > /tmp/punt.$$
        10
        11  if [ "$1" != "" ] ; then
        12    ADDRESS=$1
        13  else
        14    ADDRESS=$SENDTO
        15  fi
        16
        17  /usr/lib/sendmail $ADDRESS < /tmp/punt.$$
        18
        19  echo Punted a `wc -l </tmp/punt.$$` line news article to $ADDRESS
        20
        21  exit 0
        22
    

    This script is intended to be part of a pipeline, and it will send a copy of the stream of information either to the default address (SENDTO) or to a specified person ($1 is the first argument given to the script in this case). As shown earlier in the discussion of system prompts, any text that appears in backquotes is interpreted as a command and is executed, and the results of that command are added in its place in the subsequent command. In this case, the echo command on line 19 computes the number of lines in the specified file, and that number is then included in the output, which typically looks like this: Punted a 17 line news article.

Notice that the first character of this file is a colon. It turns out that the C shell interprets scripts only if the first character of the script is a #. Otherwise, it lets the Bourne shell (sh) run the commands, as in this case.


  1. That's all well and interesting, but I want to create a new shell script. The first step is to make sure that I'm creating the script in a directory that is included in my search path (otherwise, I won't be able to use the script as a command):

    limbo (bin) 46 : pwd
    /users/taylor/bin
    limbo (bin) 47 : echo $PATH
    .:/users/taylor/bin:/bin:/usr/bin:/usr/ucb:/usr/local:/etc:/usr/etc:
    /usr/local/bin:/usr/unsup/bin (bin) 48 :
    

    Here's a simple shell script that shows how shell scripts can be of assistance:

    limbo (bin) 86 : cat new.script
    # sample shell script
    
    echo searching for shell scripts
    pwd
    echo " "
    
    file * | grep script | sed 's/:/ /' | awk '{print $1}'
    
    exit 0
    

    This script lists the names of all files in the current directory that it identifies as shell scripts:

    limbo (bin) 88 : chmod +x new.script
    limbo (bin) 89 : new.script
    searching for shell scripts
    /users/taylor/bin
    
    bounce.msg
    locate
    new.script
    punt
    rumor.mill.sh
    

    To confirm that the new command works, look at what file reports about this same directory:

    limbo (bin) 90 : file * 
    bounce.msg:     executable shell script
    calc:   SYMMETRY i386 executable (0 @ 0) not stripped version 1
    fixit:  SYMMETRY i386 executable (0 @ 0) not stripped version 1
    locate: shell script
    massage:        SYMMETRY i386 executable (0 @ 0) not stripped version 1
    new.script:     commands text
    punt:   shell script
    rumor.mill.sh:  shell script
    say.hi: ascii text
    limbo (bin) 91 :
    
  2. A more interesting script is one that can search through all the directories in my PATH, looking for any occurrences of a specified filename:

    limbo (bin) 92 : cat locate
    # locate - find copies of a file
    #
    # this should be run by the C shell
    
    set name=$1
    
    foreach directory (`echo $PATH | sed 's/:/ /g'`)
      if ( -f $directory/$name) then
        ls -l $directory/$name
      endif
    end
    

    The foreach loop is evaluated from the inside out. Because of the backquotes, my PATH is echoed to sed, which removes the colons separating the directories. Then the C shell goes through the foreach loop once for each directory in my PATH, setting the variable directory to the subsequent value. Each time through the loop, the -f test checks for the existence of the file; if the file exists in that directory, ls -l lists some information about it.

    Here is locate at work:

    limbo (bin) 93 : locate ls
    -rwxr-xr-x  1 root        32768 May 29  1990 /bin/ls*
    limbo (bin) 94 : locate vi
    -rwxr-xr-t  7 root       163840 Nov 29  1990 /usr/ucb/vi*
    limbo (bin) 95 :
    

Pay careful attention to the backquotes and single quotes in this script.


It really would take an entire book (or two!) to describe fully all the ins and outs of shell scripts. The main idea here, however, is that if you use a lot of commands repetitively, you should make them into a command alias (if they're short) or drop them all into a shell script. In shell scripts, as in awk, $1 is always the first argument, $2 the second, and so on.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset