| 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 |