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
         ?? print
       
       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\&gt;/
       
       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.
       
 (DIR) 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.
 (DIR) Part 3: The Global Command
 (DIR) Back to the index