The vi/ex Editor, Part 2: Line-Mode Addresses | |
Whenever you want to give an editor command that will operate on | |
text that's already in the file you're editing--to delete some | |
text, change lower-case letters to capitals, write to a file, | |
etcetera--the editor needs to know what part of the file to go to | |
work on. A few commands have their addresses built in, and most | |
line-mode commands have default addresses that the editor will use | |
if you don't give an address, but that still leaves a lot of | |
occasions where you need to know how to give the editor an address | |
and what address to give. | |
Many line-mode commands are almost identical to corresponding | |
commands in visual mode; many more do similar things in different | |
ways. Most of the benefit of these duplicative command sets comes | |
from the totally-different addressing styles of line and visual | |
modes. The differing address concepts mean that an edit that | |
would be difficult or impossible to do with one mode's available | |
addresses can be a piece of cake with an address form found in the | |
other mode. | |
Since I mention "line mode" so often, you may wonder whether there | |
really is a separate mode for line editing. There surely | |
is--instead of filling your screen with text from the file you're | |
editing, this mode gives you a colon (:) prompt for your line mode | |
commands, and prints only an occasional line from the file on your | |
screen. The feel of this mode is very much like giving UNIX | |
commands from your shell prompt. Few people work in line mode | |
these days, largely because you can give most line-mode commands | |
from visual mode, but you can't give any visual-mode commands | |
while you are in line mode. Or perhaps they just prefer the | |
comfortable WYSIWYG feeling of seeing the text on screen, with | |
changes appearing as they are made. | |
But there are times when you will need to work temporarily in line | |
mode. To get to line mode when you first launch the editor, | |
invoke it by typing "ex" instead of "vi". To go to line mode when | |
you are already in the editor's visual mode, enter "Q". To get | |
back to visual mode, type "vi" followed by a carriage return. | |
Wondering why I didn't put a colon in front of that command to | |
return to visual mode, which is obviously a line-mode command? | |
Because you don't need to type that colon when you're giving a | |
command from within line mode. It may even be harmful; the rule | |
is that if you type a colon at the start of a command from within | |
line mode, there must be nothing between the colon and the command | |
name or abbreviation. Not an address, not even a space, nothing | |
at all. | |
So from this point on, I will display line-mode commands without | |
an initial colon, because you now know enough to type that colon | |
only if you're working in visual mode. And I'll leave off the tag | |
at the end of a line-mode command that reminds you to finish with | |
a carriage return because you now realize that any line-mode | |
command, given from either line or visual mode, has to end with a | |
carriage return. | |
Some of you may ask why I show line-mode command lines in | |
long-winded form, with spelled-out command names and lots of | |
whitespace instead of using abbreviations. For instance, the two | |
command lines: | |
global /^/ move 0 | |
g/^/m0 | |
are identical in their effect, and the second is surely faster to | |
type, so why do I show the first form? Because the long version | |
is much easier to follow when I'm demonstrating a new concept, and | |
almost everything here will be new to at least some of you. And | |
it's a good idea to get to know the long forms, because you'll | |
soon be learning to write editor scripts, and those scripts would | |
be as cryptic as APL to future maintenance programmers if you | |
wrote them in terse style. When I go over the roster of line-mode | |
commands, I'll tell you both the long name and one or two short | |
names for each. | |
Line-Mode Addressing | |
A SINGLE ADDRESS is often all you need with a line-mode command. | |
One address refers to just one line, which tells a command like | |
delete or substitute to operate on that one line only. A command | |
like insert or read, which puts something immediately before or | |
after a particular line, has no use for more than one address. | |
A search pattern, as discussed in the first installment of this | |
tutorial, is always an acceptable line-mode address. You put the | |
address at the start of the command line, before the command name | |
(but after the initial colon if you are giving the command from | |
visual mode), so: | |
?the [cC]at? delete | |
will erase the last previous line that contains the string "the | |
cat" or "the Cat", while: | |
/^GLOSSARY$/ read gloss.book | |
puts the contents of the file "gloss.book" right after the next | |
line in the file you're editing that contains only the word | |
"GLOSSARY". | |
There are two shorthand forms for reusing search patterns as | |
addresses. Typing "??" or "//" tells the editor to use the last | |
search pattern you used previously, and your choice of "??" or | |
"//" will set the direction of the search, overriding the | |
direction you chose the previous time you used that search | |
pattern. That is, if you type: | |
?the cat? yank | |
// delete | |
the second command will search forward, to remove the last | |
previous line containing the string "the cat", even though your | |
original use of that pattern was in a backward search. The third | |
command will search backward to find the line to print, which (by | |
coincidence) is the direction of the original search. | |
But the search pattern that those preceding abbreviations reuse | |
may not be a pattern you used to search for a line. If you ran a | |
substitute command after any pattern searches for lines, then the | |
pattern you gave the substitute command to tell it what text to | |
take out of the line is the pattern that will be reused. This is | |
so even if your substitute command began with a search pattern to | |
specify the line on which the substitution was to be | |
performed--the search to find the pattern to be replaced within | |
the line was run after the first search pattern had found the line | |
to operate on, so the search within the line was the last pattern | |
search run. So if you were to type: | |
/the cat/ substitute /in the hat/on the mat | |
?? delete | |
the second command would, in this case, delete the last previous | |
line containing "in the hat". To be sure that the pattern that | |
gets reused is the last one used to find a line, use the | |
abbreviations "\?" and "\/" to search backward and forward, | |
respectively. In all other respects these work just as typing | |
"??" and "//" do. | |
A LINE NUMBER is also a valid line-mode address. The editor | |
automatically numbers each line in the file consecutively, and | |
this numbering is dynamic--that is, whenever you add or delete | |
lines somewhere, the editor renumbers all the lines following the | |
insertion or deletion point. So if you change some text on line | |
46 in your file, and then delete lines 11 and 12, the line with | |
the text you changed is now line 44. And if you then add ten new | |
lines after line 17, the line with your changed text on it now | |
automatically becomes line 54. | |
There is never a gap or an overlap in the line number sequence, so | |
the nth line in the file is always line number n; that is, the 7th | |
line is always line number 7, and so on. (There are several ways | |
to display these line numbers, which I will expound in a later | |
tutorial installment.) To delete the 153rd line in your file, | |
just type: | |
153 delete | |
You don't use any delimiters around a line number, or around any | |
other address except a search pattern. | |
There are two symbolic line numbers and one fictional one that can | |
be used in line-mode addresses. As long as there are any lines in | |
the buffer (that is, you haven't specified a not-yet-existent file | |
to edit and failed to enter any text so far), the editor regards | |
you as being `on' one of them, usually the last line affected by | |
your latest command. A period or dot (.) is the symbolic address | |
for this line. The last line in the file also has a symbolic | |
address: the dollar sign ($). So if you should type: | |
. write >> goodlines | |
$ delete | |
the first command would append a copy of just the line you are on | |
now to a file named "goodlines", while the second would delete the | |
last line in the file you are editing. | |
A few commands put text immediately after the line address you | |
give: the append command is one of them. In order to let them put | |
their text at the very start of a file (if that is where you want | |
it), these commands can take the fictitious line number zero (0) | |
as their address. So, if you want to type some text that will | |
appear ahead of anything already in the file, you can do it with | |
either of these command lines: | |
1 insert | |
0 append | |
(Note, though, that insert and append are among the few line-mode | |
commands that cannot be run from visual mode by starting with a | |
colon, because they occupy more than one line including the text | |
to be put in.) | |
WRITING YOUR OWN LINE ADDRESSES is possible, too. You can attach | |
lower-case letters to lines as line addresses, and change the | |
attachments whenever you like. You can even use a special address | |
that is automatically attached to the last line you jumped off | |
from. | |
There are ways to mark a particular line with a lower-case letter | |
of the alphabet, and those ways differ between line and visual | |
modes. I'll be explaining all these ways in later installments of | |
this tutorial. But once a line is marked, the line-mode address | |
that refers to that line is just the single-quote character | |
followed immediately by the lower-case letter with which the line | |
was marked. So typing: | |
'b print | |
will display on the screen whatever line you have previously | |
marked with the letter b, no matter where the line is in relation | |
to where you are when you give the command. No need to tell the | |
editor whether to search forward or backward; there can be only | |
one line at a time marked with any one letter, and the editor will | |
find that line regardless. | |
The editor does some line marking on its own, too. Whenever you | |
move from one line to another by a non-relative address, the | |
editor marks the line you just left. (A non-relative address is | |
one that isn't a known number of lines from where you were.) So: | |
$ | |
/the cat/ | |
358 | |
?glossary? +7 | |
'b | |
are all non-relative addresses, and if you give any one of them, | |
the editor will mark the line you are leaving for future | |
reference. Then you can return to that line just by typing two | |
successive single quotes: | |
" | |
as a line-mode address. In theory, you can use this address with | |
any line-mode command. But it is so difficult to know for sure | |
when you left a line via a non-relative address that this address | |
form is best saved for going back to where you were when a mistake | |
moves you far away, at least until you're a wizard with this | |
editor. | |
MODIFYING ANY OF THESE ADDRESSES is possible, and there are two | |
ways to do this. The simpler way is to offset the address a | |
certain number of lines forward or backward with plus (+) or minus | |
(-) signs. The rule is that each plus sign following an address | |
tells the editor to go one line farther forward in the file than | |
the basic address, while each minus sign means a line backward. | |
So these three addresses all refer to the same line: | |
35 | |
37 -- | |
30 +++++ | |
Not that you're likely to want to modify line-number addresses | |
with counts, unless you're weak in arithmetic and want the editor | |
to do the adding and subtracting for you. But the count offsets | |
will work with any line-mode addresses, and are most often used | |
with search patterns. In any event, there is a shorthand for | |
these counts, too. A plus or minus sign immediately followed by a | |
number (single or multiple digits) is equivalent to a string of | |
plus or minus signs equal to that number, so that these two | |
addresses are the same: | |
/^register long/ ++++ | |
/^register long/ +4 | |
Take note that the "4" in the second example does not mean "line | |
number 4", as it would if it appeared by itself as an address. | |
After a plus or minus sign, a number is a count forward or | |
backward from where the primary address lands (or if there is no | |
primary address before the count, from the line you are on when | |
you run the command). | |
Note also that this is one of the few places in line-mode commands | |
where you may not insert a blank space. The number must start in | |
the very next character position after the plus or minus sign. If | |
you violate this rule, the editor will uncomplainingly operate on | |
some line that definitely is not the line you expected. | |
The second style of address modifier is used where you want to do | |
a search that's complex. Let's say you want to go forward in the | |
file to delete a line that starts with "WARNING!", but not the | |
first such line the editor would encounter; you want the second | |
instance. Either of these command lines will do it: | |
/^WARNING!/ ; /^WARNING!/ delete | |
/^WARNING!/ ; // delete | |
A semicolon (;) between two search patterns tells the editor to | |
find the location of the first pattern in the usual way, then | |
start searching from that location for the second pattern. In | |
this case, the first search pattern turned up the first instance | |
of a line starting with "WARNING!", and the second search pattern | |
led the editor on to the second instance. | |
A very significant point here is that this combination of two | |
search patterns, either of which could be a line address in | |
itself, does not tell the editor to delete two lines. The | |
semicolon means that the first pattern is merely a way station, | |
and that the single line found by the second search pattern is the | |
only line to be deleted. In brief, what looks like addresses for | |
two lines is actually only an address for one. (This is not what | |
the official documentation for this editor says, but the | |
documentation is just plain wrong on this point.) | |
But that's just the start of what you can do. You are not | |
restricted to just two addresses. I've used up to ten of them, | |
all separated by semicolons, to reach one specific line. As an | |
example: | |
?^Chapter 3$? ; /^Bibliography$/ ; /^Spinoza/ ; /Monads/ | |
will bring me to the title line of Spinoza's first work with | |
"Monads" in the title, in the bibliography for Chapter 3. | |
Nor are you limited to search pattern addresses when putting | |
together a semicolon-separated address string. If you want to | |
reach the first line following line 462 that contains the word | |
"union", typing: | |
462 ; /\<union\>/ | |
will bring you there. And any of the addresses can take numerical | |
offsets, so: | |
462 +137 ; /register int/ --- | |
is also a legitimate address string. | |
But there are two unfortunate limitations on using | |
semicolon-separated address strings. The lesser problem is that | |
such a string can use "line zero" as an address only if the | |
command following the address string could take line zero by | |
itself as its address. That is, you can't even start at line zero | |
and then proceed elsewhere with additional addresses, unless the | |
command can operate from line zero. So: | |
0 ; /Spinoza/ +++ ; /Kant/ delete | |
which looks like a reasonable way to be sure your search will | |
find the very first "Spinoza" in your file, will actually fail | |
with an error message about an illegal address. | |
The larger misfortune is that each address in a semicolon- | |
separated string must be farther down in the file than the one | |
that precedes it. (This means the actual location found, after | |
applying any plus-sign or minus-sign offset.) You cannot | |
move backward within the series of way points. | |
But that does not mean that you cannot use a backward search | |
pattern within the string. The first address can be a backward | |
search, of course. And a subsequent address can search backward | |
if you are certain that the line it finds will actually be more | |
forward in the file. For example, you may know that a certain | |
backward search will wrap around to the bottom end of the file | |
before it finds a match. A common example would be: | |
1 ; ?Spinoza? ; /Hegel/ yank | |
Beginning a backward search from the first line in the file means | |
that the search must start with the last line in the file due to | |
wraparound, which guarantees that the search will yank the "Hegel" | |
line that follows the vary last "Spinoza" line in your file. | |
Also, you can use a plus-sign offset after a backward search when | |
you are certain that the line finally found after the offset is | |
applied will be farther down in the file than the preceding way | |
point had been. Thus, if I want to find the first mention of | |
Hegel in Chapter 8 that is at least 120 lines after the last | |
mention of him in Chapter 7, I can type: | |
/^Chapter 8$/ ; ?Hegel? +119 ; // | |
If a command with this address fails and gives an error message | |
about a bad address, I'll know that the last mention of Hegel in | |
Chapter 7 is more than 120 lines before the end of the chapter, so | |
the very first mention of his name in Chapter 8 is what I'm | |
looking for. In that case, the address: | |
/^Chapter 8$/ ; /Hegel/ | |
is all that my command needs. | |
The situation with forward searches inside a semicolon-separated | |
address string is a mirror image of what I've just said. A forward | |
search can take a minus-sign offset if you know that the offset is | |
small enough that the line found will be further down than the | |
last way point. But a forward search will fail, even with no | |
offset or a plus-sign offset, if wraparound makes it find a line | |
earlier in the file than the way point from which it began. | |
Addressing a Section of Text | |
TWO ADDRESSES CAN ALSO STAND FOR A RANGE OF LINES. When two | |
addresses are separated by a comma rather than a semicolon, the | |
meaning changes radically. (What a difference a dot makes!) | |
Often you will want a line-mode command to act on a series of | |
successive lines. For example, you may want to move a stretch of | |
text from one place to another. To do this, you give the address | |
of the first line you want the command to act on, followed by the | |
last line it should act on, and separate the two addresses with a | |
comma. So, the command: | |
14 , 17 delete | |
will delete line 14 and line 15 and line 16 and line 17. You can | |
see that putting more than two addresses in a comma-separated | |
address string would be pointless. The line mode of this editor | |
is discreet if you ignore this and string together three or more | |
addresses with comma separation: it uses the first two addresses | |
and discards the rest. | |
Any line-mode addresses may be used with a comma. All of the | |
following combinations make sense: | |
'd , /^struct/ | |
257 , . | |
?^Chapter 9$? , $ | |
The first address combination would cause the command that follows | |
it to operate on the section starting with the line you have | |
previously marked "d" and ending with the next forward line that | |
begins with "struct", inclusive. The second combination covers | |
line 257 through the line you are on now. The third goes backward | |
to include the previous line containing only "Chapter 9", and | |
forward to include the very last line in your file; plus all the | |
lines in between, of course. | |
There are limitations on this technique, too. The primary one is | |
that the address after the comma (after any offsets, of course) | |
cannot be earlier in the file than the address before the comma. | |
That is, the range of lines must run forward from the first | |
address to the second address. So the command: | |
57 , 188 delete | |
is just fine, while the similar-looking command: | |
188 , 57 delete | |
will only produce an error message. (But if the two addresses | |
happen to evaluate to the same line, there is no problem. The | |
command will silently operate on the one line you've specified.) | |
As you work up to more sophisticated line-mode addresses, you may | |
get unexpected error messages about the second address being prior | |
to first address, when you don't see how you could have | |
anticipated that the addresses would evaluate that way. That's no | |
disgrace, and the solution is simple. After you've looked over | |
the addresses you used, and you're certain that they are the ones | |
you want, just type the command in again with the two addresses in | |
reverse order. That is, if: | |
642 , /in Table 23/ delete | |
has failed, giving an error message that the lines are in the | |
wrong order, then: | |
/in Table 23/ , 642 delete | |
will solve that problem. | |
The last limitation is that when you use search patterns on both | |
sides of a comma, the second search starts from the current line | |
just as the first search did; it does not start from the line that | |
the first search found. There's a way around that, though, that | |
involves using one or more semicolons along with a comma. | |
A semicolon-separated address string can be used anywhere in line | |
mode that you would use a single address. One very useful | |
technique is to use these address strings on one or both sides of | |
a comma, to indicate a range of lines to be affected. Remember | |
that an address string separated by semicolons is the address of | |
just one line, so this one line can be the start or the end of a | |
range of lines. For example, in: | |
/^INDEX$/ ; /^Xerxes/ , $ write tailfile | |
?^PREFACE$? ; /^My 7th point/ , ?^PREFACE$? ; /^In summary/ -- delete | |
the first command would write the latter part of the index to a | |
new file, while the second could be used to remove a section of a | |
book's preface. | |
And that brings up the solution to our previous obstacle; the | |
second search's starting point. If you want the search after the | |
comma to begin from the point the first search found, use the | |
first search pattern followed by a semicolon as the start of your | |
after-the-comma search string, as in either of: | |
?Stradivarius? , ?Stradivarius? ; /Guarnerius/ | |
?Stradivarius? , ?? ; /Guarnerius/ | |
In view of the rules about not going backward in line-mode address | |
strings, I'd better clarify the way these limitations work when | |
you combine semicolon and comma separation, as in these two | |
examples. All but the first of the way points in each | |
semicolon-separated string must be in the forward direction, of | |
course, but the start of the second semicolon-separated string may | |
be prior to any of the addresses in the first such string, that | |
is, the one-way meter resets itself at the comma point. And using | |
semicolon-separated strings on both sides of a comma only requires | |
that the final landing point of the second semicolon-separated | |
string not be earlier in the file than the final landing point of | |
the first; the relative locations of the way points don't matter | |
to the comma. To clarify this, consider a couple of odd-looking, | |
and useless, but very lucid examples. The combination: | |
125 ; 176 ; 221 , 32 ; 67 ; 240 | |
looks invalid due to the backward jump from line 221 to line 32, | |
but is actually a perfectly good address. The back jump comes | |
right after the comma, where it is all right. But: | |
125 ; 176 ; 221 , 32 ; 67 ; 218 | |
will produce an error message, because the final landing point of | |
the first semicolon-separated string, line 221, falls later in the | |
file than the final landing point of the second semicolon- | |
separated string, line 218. | |
Now, a note about default addresses. I've already mentioned that | |
most line-mode commands that can take an address have a "default" | |
address built in, which tells the editor where to run the command | |
if you don't give an address with it. Each command has its own | |
default address, which may be the current line, the current line | |
plus the one following, the last line of the file, or the entire | |
file. | |
The comma separator has default addresses of its own. They are | |
the same regardless of what command is being used, and they | |
override any command's own default address. If you put a comma | |
before a command and don't put an address before the comma, by | |
default the address there is the current line. In the same way, | |
if you leave out the address after the comma, the default there is | |
also the current line. You can even leave out the address in both | |
places and use the current-line default in both: that means the | |
implied address is "from the current line to the current line", | |
which makes the current line the only line the command will | |
operate on. So every one of the following command lines: | |
. write >> goodlines | |
. , . write >> goodlines | |
, . write >> goodlines | |
. , write >> goodlines | |
, write >> goodlines | |
will do exactly the same thing: append a copy of just the current | |
line in the file you're editing to another file named "goodlines". | |
Finally, there is one special symbol that represents a | |
comma-separated address combination. The percent sign (%) has the | |
same meaning as 1,$ as a line-mode address combination. Both | |
refer to the entire file. | |
Now You Try It | |
Before you try the complex aspects of line-mode addresses in | |
actual editing situations, here are some problems you can build | |
yourself up on. For each problem I've included a solution that | |
will work fairly efficiently. | |
1. How can you tell the editor to delete the line that holds | |
the very last instance of "EXPORT" in your file? The solution | |
is straightforward once you know where to start searching. | |
2. Suppose you want to delete the very first line in the file | |
with "EXPORT" on it, and that just might be line 1. You can't | |
start the search from line zero because the delete command | |
cannot take line 0 as an address. When you type the address | |
string "$ ; /EXPORT/" to use wraparound, you get an error | |
message asserting that the search pattern found a line prior to | |
the line found by the "$" address that appeared first, which is | |
what you'd expect. How can you tell the editor to find and | |
delete this line? The solution requires just a bit of | |
creativity. | |
3. If you use the address "?abc? , /xyz/", it includes the two | |
lines the searches (for "abc" and "xyx") find, as well as all | |
the lines between them. How would you specify that you want | |
the affected lines to go up to, but not include, the lines the | |
two searches find? In this case the solution is simpler than | |
you might think. | |
Solutions | |
Coming Up Next | |
The next installment of this tutorial will deal with the global | |
commands--they're just too much to absorb right after the | |
mind-numbing collection of address forms we've just gone through. | |
And to give you more scope for using all these address forms, I'll | |
also cover line-mode commands themselves, particularly the ones | |
that have more capabilities than you suspect. | |
Part 3: The Global Command | |
Back to the index |