I Heart vi
Volume Number: 22 (2006)
Issue Number: 12
Column Tag: Mac in the Shell
I Heart vi
Text editing in a shell
by Edward Marczak
Introduction
It's amazing that a basic function like text editing can be so...obtuse. vi and emacs have confounded new users since they arrived on the scene. However, editing text is such an important part of any Unix or Unixlike system, that I'd be remiss if I didn't address it. This month, I'll cover basic vi enough to make you comfortable the next time you've sshed into a remote machine and need to edit a file.
Growing Up
Practically each month, this column asks you to type something into a text editor. Of course, this can be TextWrangler, SubEthaEdit or even Dreamweaver or the XCode IDE. But if you already have a shell open, or, only have the option of a shell, then an editor like vi, emacs or pico are your best options. I'm well aware of the vi/emacs debates. Emacs is truly a Swiss Army Knife of an application. Perhaps I'll cover it someday. However, for some reason, I just dug into vi more, and have stuck with it; Turns out to have been a useful choice. If you've ever needed to alter privileges for sudo users, you'll note that the 'right' way to edit the sudoers file is by using visudo basically, a stripped down version of vi. On other Unix systems, there's typically a vipw and vigroup app. All three, by default, use vi as the editor. Also, ever notice which editor you use when editing crontabs with crontab e? So, vi is very good to know at least the basics.
Quick little secret before we start: traditional vi is pretty much gone, and has been supplanted with vim, VI Improved. You'll note that vi is simply a link to vim:
$ ls ld /usr/bin/vi*
lrwxrxrx 1 root wheel 3 Jan 18 2006 /usr/bin/vi > vim
lrwxrxrx 1 root wheel 3 Jan 18 2006 /usr/bin/view > vim
rwxrxrx 1 root wheel 2060380 Dec 25 2005 /usr/bin/vim
lrwxrxrx 1 root wheel 3 Jan 18 2006 /usr/bin/vimdiff > vim
rwxrxrx 1 root wheel 1068 Dec 25 2005 /usr/bin/vimtutor
rxrxrx 1 root wheel 34472 Dec 25 2005 /usr/bin/vis
OS X v10.4 ships with vim v6.2. You can download the latest version 7 and compile it up, as it compiles and runs cleanly under OS X. However, there's no need to do that to follow this particular column.
The Tower That Ate People
If you've never used vi, we'll take it stepbystep. So, open up your favorite terminal app and get a shell. You may as well stay in your home directory, as we're not going to make too much of a mess.
To start the editor, simply type vi, and you'll be greeted with a startup screen. Unlike Word or Pages, you can't simply start typing. Well, you can, but each keystroke may not do what you expect. Those of us that go back to Mac OS 9 will remember modal dialogs. Basically, a modal dialog box stopped you from using anything else until you acknowledged it. You were put in the mode of having to deal with whatever message it presented. vi is a modal editor. You'll either be in one of three modes: normal mode, 'edit' mode, or ex command mode. What you're looking at now is normal mode: vi awaits your instruction.
Press i. Now, I should tell you here that commands in vi are case sensitive. Typically, the lowercase version means one thing, and the uppercase/shifted version negates, or is the opposite of the lower case (or, nonshift) version this makes commands a little easier to remember. So, you've pressed i, and you now see " INSERT " at the bottom of the window. Great! Your first vi command. No sweat, right? Now you're in insert mode. This is pretty much what you expect. Go ahead, type. How about we all type the same thing. We'll start out easy; try this, pressing return at the end of each line, mistakes and all:
trust a few people
love all
do wrong to none
Not too terrible, right? Pressing the escape key on your keyboard will put you back in normal mode. The " INSERT " should disappear.
Now, I didn't get the quote quite right. Let's fix it. Apple has nicely mapped most keys sanely; they do what you expect. This includes the arrow keys. However, this may not be the case on all systems, so, if you'd like to get used to the vim way, I'll show you now. This will also help your Nethack skills (http://www.nethack.org). k is cursor up, j is cursor down, while h moves the cursor left, and l moves it right. Perhaps not the easiest to remember. Different people have different ways to remember this, but, I simply suggest a Postit note and some practice. Why these keys? Vim and vi strive to be efficient editors. I love to use vi as I never have to take my hands off of the keyboard, nor stray too far from the home row. That's efficiency. If you're 100% Macdedicated, though, the arrow keys will suit you just fine, too. Digression aside, please move the cursor to the first character, the 't' in 'trust'.
Well, if that's the beginning of a sentence, it should be uppercase. There's two ways to tackle this, and we'll start with the easier of the two. With the cursor on the 't', press ~ (tilde). This toggles the character under the cursor between upper and lowercase, and then advances the cursor. Ah, but wait, that's not the correct beginning of that quote! With your cursor on that line, type dd, which will delete the line and place it in the yank buffer. Now, press p which pastes the contents of the yank buffer on the line following.
Now, our capital letter leads the second sentence, which naturally isn't right. Twiddle it back using the tilde key (~). Cursor up and onto the 'l' in 'love', and we'll replace it with an uppercase version this time, though we'll do just that: replace. Type r, followed by 'L'. r will replace one character. So, when you typed the 'L', you simply replaced what was there. Time for the correct punctuation.
With the cursor still on the first line, type A, for 'append to end of line' (make sure it's capital A!). Your cursor will jump to the end of the line and you'll enter insert mode look for the " INSERT " message in the status line. Type a comma, and press escape to get back into normal mode.
Cursor down to the second line, and then right to the 'p' in 'people'. Delete, one character at a time using the x command, the word 'people', but leave the space after 'few'. Replace the space with a comma, using r, followed by a comma. At this point, you should have:
Love all,
trust a few,
do wrong to none
...with your cursor on the comma after 'few'. Cursor down to the third line and type $ jump to the end of the current line. Press a (the letter 'a') append after the cursor and type a '.' (period). (Those of you paying attention will realize that this could have been done in one step with capital A...but how else was I to stick in the $ command to jump to the end of a line?) Press escape to go back to normal mode.
Cursor up to the first line and press J (capital J). This will join this line with the one following. Do it again, and you'll end up with one single line:
Love all, trust a few, do wrong to none.
And Through The Wire
So, now you have the very basics of editing with vi. I mentioned that vi begat vim, but I didn't yet tell you that ex begat vi. The foundation of certain commands are ex commands. You enter ex command mode, by typing a colon (":"). You'll see a colon appear on the status line, and the cursor will jump down to immediately follow it. Type w ws.txt. 'w' is for 'write', and will write the file 'ws.txt'.
If you need to take a break, you can type :q to quit. Reenter vi while loading the file like this: vi ws.txt.
We've looked at three ways to get into insert mode so far: 'i', for insert in place, 'a' for append at the cursor and 'A', which appends at the end of the current line. Here's one more: o. This opens a new line beneath the current line. Try it now.
Don't forget! vi inherits all of the integration that using Terminal.app brings OS X users. The best is draganddrop. While in insert mode, you can pick up a text or .clipping file and drop it on the terminal. The contents will insert at the cursor.
Figure 1: Dragging a .clipping file to Terminal.app while editing in vi
Try it now, while you're still in insert mode!
At this point, I encourage you to quit vi :q and type vimtutor. It's an interactive vi tutorial that takes about 1520 minutes. Good, basic practice and tips.
More Than This
vimtutor does a good job of covering the basics. Let me wrap up with some of my favorites that neither I or vimtutor have introduced you to.
:shell this ex command lets you escape to a shell, do your work, and then when you exit, you'll find yourself right where you left off. Now, certainly, this function has lost some value in the days of multiwindow machines, but sometimes, it's really handy. It let's you get a lay of the land, and then pick up right from where you left off.
vimtutor shows you how to run an external shell command, but at times, there's even something more useful: running a command and inserting its output at the cursor. To preface this, you can read in a file using the r ex command. You can even read in the file you're working on. Try it now with :r ws.txt. You'll see the contents of the file as it is on disk appear inserted into your file after your cursor. Similarly, you can read in the output of a shell command with r !. Insert all of the users from Directory Services into your file with:
:r !dscl localhost list /Search/Users
This comes in very handy. Instead of running a command, redirecting output to a file and then editing that file, you can do it in one step in vi.
vimtutor also shows you how to search using the / normal command. If you didn't run through vimtutor, do it now. Really. Here's two things they didn't get into fully. Load up vimtutor, as it provides ample text for us (quit your current session with :q, and then type vimtutor at the shell prompt). Put the cursor on the first occurrence of the word "commands". You should see several of them. Press * (asterisk). vi automatically starts searching on that word, and jumps to the next occurrence. You can jump between occurrences with n and N. n jumping to the next (forward) occurrence, and N jumping backward.
Anyone familiar with sed will feel right at home with vim's search and replace function. vimtutor showed you searching with /. Search and replace is accomplished with :% s/old/new/g. The "%" represents the entire file. Without it, you're only searching the current line. Additionally the "/g" means global, and without that flag, only the first occurrence on each line would be replaced. You can also use the c flag to have vi ask for a confirmation. Go to the top of the file and try this:
:% s/commands/friends/gc
All occurrences of "commands" will be changed to "friends" and you'll be asked for confirmation of each change. You may notice that this substitution works surprisingly well!
Finally, one option I use quite often: changing line endings. Since a file is just a stream of bytes, the computer needs some way to recognize when humans want to see a new line. Unfortunately, the three major platforms all decided on different ways. Unix and Unixlike systems use a linefeed character (LF), ascii 0x0A. DOS uses both a new line (NL, or carriage return CR) and line feed. When DOS sees CRLF, ascii 0x0D 0x0A, it knows to show us humans a new line. Finally, Mac systems traditionally have used a new line only. The current state of line endings under OS X is a bit of a mishmash. Most GUI apps still use CR, where most (if not all) shell apps use LF.
Many times, you'll receive a DOS lineendingformatted file that should have Unix line endings. vi handles this with aplomb. Use :set ff=unix and then write the file with :w. Done! Go the other way with :set ff=dos. Sometimes this shows up as ^M (ctrlM) characters that's the extra CR character. Just change the file type and write it out.
Don't Give Up
vi is a craftsman's tool. Like any tool, it requires a little work and practice to learn in depth, or to make second nature. I hope next time that you're into a remote system and need to edit a file, or for some reason are forced to use vi, that you'll be confident in your usage. In the spirit of self exploration, take a look at all of the settings that can be changed to tailor vi to your style by trying :set all.
Be aware that there is a GUI version of vim available at <http://www.macvim.org>. It may not be a GUI app the way you think of one, but it is integrated with the Aqua environment. Frankly, though, I never really saw the point in X11 or GUI versions of vi or emacs. In those environments, you'll have access to a shell, so why not use it as intended?
Media of the month: Up, by Peter Gabriel. A masterpiece. Seriously. If, for some reason, that doesn't do it for you, just take some time to put on some headphones (real cans, not those iPod things), lay down, and really listen to some music.
See everyone next month at MacWorld, I hope! I'm presenting two sessions, and will be hanging around the MacTech booth, so please make sure you stop by and say hello!
References
The vi man page
Vim home: http://www.wim.org
Nethack: http://www.nethack.org
"All's Well That Ends Well," William Shakespeare
Ed Marczak owns and operates Radiotope, a technology consulting practice with a focus on business process enhancement, network and system integration, and, more generally, all things Mac.