Mac OS X Command Line 101
by Richard Burton
Command Line History & Editing Your Commands
Part XVII of this series...
November 22nd, 2002
Shome mishtake, shurely? - catchphrase of Private Eye magazine
This series is designed to help you learn more about the Mac OS X command line. If you have any questions about what you read here, check out the earlier columns, write back in the comments below, or join us in the Hardcore X! forum.
If you've been following this column, not only are you a person of impeccable taste, you're also likely to have typed a mistake on the command line. That's nothing to be ashamed of, we're only human. (Well, you are.) You've probably miss-typed a command and been faced with retyping the whole command again. You might be able to do a copy-and-paste, if the previous command is still on the screen. However, that is not always possible. And if you'll recall, Unix is more than a decade older than the first available GUI (the one on the Apple Lisa); with no mouse, doing a copy-and-paste is not so intuitive. What's needed is a way to move back and forth through a list of previous commands.
You will not be surprised to read that this is possible. And if you know Unix, you will not be surprised to read that there is more than one way to do this.
tcsh provides a very simple mechanism to do this using the arrow keys. It's so simple, in fact, that even a rocket scientist can grasp it. (And I oughta know ...)
Memories...
Open a terminal window, and type in a few commands: ls, pwd, who, whatever floats your boat. You can now move up and down the list of commands you just typed using the up and down arrow keys. You can also, shock of shocks, move the cursor back and forth across a command with the left and right arrows. It's very similar to moving around pico, the simple editor I discussed in the previous column. It is not pico, though; the pico commands (the CONTROL- characters) don't do the same thing. Sometimes they do nothing, sometimes they cause some goofy behavior, so until you are ready for more than the arrow keys, it's best to avoid them. For the morbidly curious, you can find out what they are by typing bindkey. Make sure you pipe it through the more command, it takes several screens to list them all. You might also want to keep a cool, damp cloth for your forehead within easy reach.
Auto completion is a life saver
One other feature is command completion, which is related to command line editing. Sorta. In your terminal window, go to your home directory:
[localhost:~] dr_unix% cd
[localhost:~] dr_unix%
Now let's look at the directory's list of files, using the -F flag of ls so that directories are denoted by a trailing '/':
[localhost:~] dr_unix% ls -F
Adam.txt Library/ Public/ personal/ testfile
Desktop/ Music/ Sites/ temp.html who_list
Documents/ Pictures/ login test_1.txt
[localhost:~] dr_unix%
Better, use our new tool fgrep to filter out those files which are not directories:
[localhost:~] dr_unix% ls -F | fgrep '/'
Desktop/
Documents/
Library/
Music/
Pictures/
Public/
Sites/
personal/
[localhost:~] dr_unix%
Say you want to change your default directory to Library. If you are lazy and/or a bad typist, and who isn't, you don't have to type the whole command:
[localhost:~] dr_unix% cd Library
[localhost:~/Library] dr_unix% pwd
/Users/dr_unix/Library
[localhost:~/Library] dr_unix%
Instead, you can simply type (without hitting RETURN):
[localhost:~] dr_unix% cd Lib
If you now hit TAB, you get:
[localhost:~] dr_unix% cd Library/
Since there is only one file in the directory that starts "Lib", tcsh will 'complete' the command when you hit the TAB.
[localhost:~] dr_unix% cd Library/
[localhost:~/Library] dr_unix% pwd
/Users/dr_unix/Library
[localhost:~/Library] dr_unix%
Neat, huh?
Get it on, bang a gong...
Now, most of us aren't complete geeks (and some who are won't admit it), so this will take a lot of us a long way. However, there are more powerful forms of command line editing available. Go to your terminal window, and type the ls command:
[localhost:~] dr_unix% ls
Adam.txt Documents Music Public login temp.html testfile
Desktop Library Pictures Sites personal test_1.txt who_list
[localhost:~] dr_unix%
Now, type !! and hit return:
[localhost:~] dr_unix% ls
Adam.txt Documents Music Public login temp.html testfile
Desktop Library Pictures Sites personal test_1.txt who_list
[localhost:~] dr_unix% !!
ls
Adam.txt Documents Music Public login temp.html testfile
Desktop Library Pictures Sites personal test_1.txt who_list
[localhost:~] dr_unix%
The '!!' command will run the previous command. Notice how, after you hit RETURN, it showed what the command was before it executed it? If you keep typing '!!' over and over again, you can run the ls command from here to Goshen. In fact, tcsh is clever enough that you can add to the previous command:
[localhost:~] dr_unix% ls
Adam.txt Documents Music Public login temp.html testfile
Desktop Library Pictures Sites personal test_1.txt who_list
[localhost:~] dr_unix% !! | grep t
ls | grep t
Adam.txt
Desktop
Documents
Pictures
Sites
temp.html
test_1.txt
testfile
who_list
[localhost:~] dr_unix%
History
What's behind all this is tcsh's built-in history commands. Each terminal keeps track of a list of the previously run commands, storing up to the number denoted by the "history" variable:
[localhost:~] dr_unix% echo $history
100
[localhost:~] dr_unix%
Of course, history being just a variable, you can set it to whatever you want, viz:
[localhost:~] dr_unix% set history=20
[localhost:~] dr_unix% echo $history
20
[localhost:~] dr_unix%
If you want to see the list of commands in your history, just type history:
[localhost:~] dr_unix% history
8 19:39 cd Library
9 19:39 pwd
10 19:49 cd ..
11 19:59 cd Library/
12 19:59 pwd
13 20:01 cd
14 20:01 echo $history
15 20:01 ls -a
16 20:01 more login
17 20:01 w
18 20:02 ps
19 20:03 clear
20 20:03 ls
21 20:04 ls
22 20:12 ls
23 20:12 ls -a
24 20:19 echo $history
25 20:20 set history=20
26 20:20 echo $history
27 20:23 history
[localhost:~] dr_unix%
Notice that this give you a list of commands, each with an event number and a timestamp. You can also print just the last n commands by giving history an argument:
[localhost:~] dr_unix% history 5
24 20:19 echo $history
25 20:20 set history=20
26 20:20 echo $history
27 20:23 history
28 20:26 history 5
[localhost:~] dr_unix%
!! is a special/default case. If you want to rerun a specific command, you simply type !n, where n is the event number (the sequential number that is at the beginning of each line):
[localhost:~] dr_unix% !25
set history=20
[localhost:~] dr_unix%
If you want to repeat the previous command which started with a string, just type a ! followed by the string:
[localhost:~] dr_unix% !history
history 5
27 20:23 history
28 20:26 history 5
29 20:32 set history=20
30 20:35 more login
31 20:35 history 5
[localhost:~] dr_unix%
(And yes, I did sneak another command in there on you.) This is useful, but not always what you want. Often, you will be using several commands in succession on a particular file, and the name of which will not be the first part of the command. Let's say your history file looks something like:
54 12:45 cd Documents/novels/
55 12:45 ls
56 12:46 vi big_fish_little_blonde.txt
57 12:49 nethack
58 14:32 ls -l big_fish_little_blonde.txt
59 14:32 wc -l big_fish_little_blonde.txt
60 14:32 vi big_fish_little_blonde.txt
61 14:33 nethack
62 16:09 pwd
(If that looks silly, that's because it's based on two inside jokes, and darned obscure ones, known to some people in our forums.) If you want to repeat the last command on the file, put ?s around the string:
[localhost:~] dr_unix% !?big_fish?
That will take you back into vi, editing the file so named
Multiple histories
If you open several terminals at once and fool around with each one's command history, you will notice that each terminal's command history is kept separate from the others. However, if you want to open another terminal later and have it know the history you now have, this can be done. (Didn't you just know it?)
If you set the savehist shell variable (by simply typing set savehist), the shell will save its history list to the file ~/.history when you log out. The next time you log in, this file is read and the list is placed into the new shell's history list. If savehist is not given an argument, the entire list is saved to ~/.history; if you give it a value (which should be less than the value of $history), it will save that many of the most recent commands.
More useless command stuff
There are more things in manipulating command history, but I have found them useful in rare cases, if ever. Given all that there is and how rarely you use it, the remaining commands are ignorable. But if you just half to know them all, a good book on csh and/or tcsh should list those commands.
Editing Your Commands
As I've mentioned before, Unix has a couple of text editors that are powerful. What's more, they inspire great loyalty or derision, depending on your prejudices. Now, given the nature of Unix, or rather Unix developers, you would expect that someone could choose to use vi or emacs commands to edit your commands, search through the history lists, etc. What else would you expect from such charming rogues (vi) and rakish chaps (emacs), plus the occasional handsome brute?
Well, you can, and it is done with the bindkey command. bindkey will let you set your editing commands to either emacs (bindkey -e) or to vi (bindkey -v). For now, we will look at the vi version, for two reasons: vi has already been covered in this series, and the emacs version doesn't require any explanation, once you know emacs.
As I'm sure you'll recall, vi has a command mode and an input mode. To enter the input mode, you need to enter one of several commands (i, a, R, etc.). To return to command mode requires hitting the ESCAPE key. Now, if you think about it, normally you just want to start typing commands and not have to worry about editing the commands. It would be pretty silly to have to start each command by typing an 'i' to enter input mode. Therefore, when you set your key binding to vi, you are placed into input mode automatically after each command. To enter command mode, you must hit the ESCAPE key.
Let's try this, shall we? Open a new terminal session, and set your key bindings to vi mode.
[localhost:~] dr_unix% bindkey -v
[localhost:~] dr_unix%
(Those of you who know emacs are welcome to set your key bindings with a -e.)
Now type a few familiar commands:
[localhost:~] dr_unix% pwd
/Users/dr_unix
[localhost:~] dr_unix% w
3:50PM up 1:37, 3 users, load averages: 0.60, 0.43, 0.26
USER TTY FROM [email protected] IDLE WHAT
dr_unix co - 2:15PM 1:37 -
dr_unix p1 - 2:20PM 0 -
dr_unix p2 - 3:39PM 0 -
[localhost:~] dr_unix% date
Sat Oct 26 15:50:09 EST 2002
[localhost:~] dr_unix%
Now hit ESCAPE to enter command mode. You can move up and down your history list using vi's "j" and "k" commands. You can also move within a command using the movement commands like "w", "E", and so on. This includes the "/" command to search for a command which matches a regular expression. And you can use any vi command to edit that command. As a plus, tcsh lets you use the arrow keys at the same time.
Congratulations, you are now well on your way to being a Unix shell power user. Next, we'll look at handling multiple processes.
You are encouraged to send Richard your comments, or to post them below.
Most Recent Mac OS X Command Line 101 Columns
Command Line History & Editing Your Commands
November 22nd
Pico: An Easy To Use Command Line Editor
November 1st
Understanding The "grep" Command In Mac OS X
October 4th
Command Line History & Editing Your Commands
September 6th
Mac OS X Command Line 101 Archives
Back to The Mac Observer For More Mac News!
Richard Burton is a longtime Unix programmer and a handsome brute. He spends his spare time yelling at the television during Colts and Pacers games, writing politically incorrect short stories, and trying to shoot the neighbor's cat (not really) nesting in his garage. He can be seen running roughshod over the TMO forums under the alias tbone1.
|