| The vi/ex Editor, Part 4: The Subtle Substitute Command | |
| Making Changes Within Lines | |
| A Few More Metacharacters | |
| Replacement-Pattern Metacharacters | |
| Other Uses for Substitution | |
| A Start on Script Writing | |
| Don't Lose Your Files | |
| Reader Feedback | |
| The Next Installment | |
| After the :global command, which we discussed in the previous | |
| installment of this tutorial series, the :substitute command is | |
| line mode's most subtle and complex tool. When I've gone over | |
| those complexities we'll be ready to deal with techniques and | |
| tricks of building powerful line-mode command strings. | |
| Making Changes Within Lines | |
| Most of you already know the :substitute | |
| command by its shortest abbreviation :s | |
| and use it in roughly this form: | |
| s/previous/former/ | |
| %s/Smith/Lee and Smith/ | |
| to make some change within the line you are on, in the first case, | |
| or change every instance in the file in the second. If you use | |
| both forms you are already ahead of the game. Too many class | |
| instructors and textbook writers try to tell you that the way to | |
| change some phrase throughout the file is to type something like: | |
| global/Smith/s//Lee and Smith/ | |
| This is wasteful nonsense. Both forms accomplish exactly the same | |
| thing, but the second version involves extra typing for you and an | |
| extra run through the file for your computer. It does not matter | |
| that not every line in your file will contain a "Smith" to be | |
| changed -- the :substitute command will execute properly in | |
| either version, and quietly if even one line in the file has a | |
| "Smith" it can change. | |
| But neither form as it stands is sure to change every "Smith" in | |
| the file. The :substitute command is set up to change only the | |
| first example of the string it finds on any particular line, so a | |
| line in the text that originally read: | |
| inure to Smith's benefit only if Smith shall | |
| will be changed by either version of the command to read: | |
| inure to Lee and Smith's benefit only if Smith shall | |
| Line mode has a built-in solution for this problem: place a | |
| lower-case letter "g" at the very end of the command, immediately | |
| after the last "/" mark, in order to make the change on every such | |
| string in each line. So typing this: | |
| % substitute /Smith/Lee and Smith/g | |
| will make that text line come out as: | |
| inure to Lee and Smith's benefit only if Lee and Smith shall | |
| Finer tuning of the instances can be done by a little trickery. | |
| Suppose you are working on tables, and want to change only the | |
| very last "k37" on each line to "q53". This command will do it: | |
| % substitute /\(..*\)k37/\1q53 | |
| If this seems surprising, remember that in a search pattern with a | |
| wild card, the editor always extends the match to the greatest | |
| length it can. In this case that means the string starting at the | |
| beginning of the line and ending with the last "k37" in the line. | |
| Now you should be able to extend this example. What command would | |
| change only the second-to-last "k37" on each line? This requires | |
| a shrewd guess from you, so I've written a solution you can | |
| compare to your own. | |
| A Few More Metacharacters | |
| You probably already know that you don't always have to type the | |
| search pattern that indicates the text to be replaced by a | |
| substitution command. If you want to reuse your very last search | |
| pattern, whether that was in a substitution command or not, you | |
| can use an empty search pattern string to stand for the last | |
| search pattern, so the two commands below are actually identical. | |
| /Murphy/ substitute /Murphy/Thatcher/ | |
| /Murphy/ substitute //Thatcher/ | |
| Either command will go to the next line containing "Murphy" and | |
| there replace the first "Murphy" with "Thatcher". | |
| Within a substitution command's search pattern to find the text to | |
| be removed, all the normal search-pattern metacharacters apply. | |
| So do two more that are reserved only for substitution commands: | |
| the "\(" and "\)" metacharacters. | |
| These two metacharacters don't match anything themselves, so: | |
| substitute /^The cat and small dog show/ | |
| substitute /^The \(cat\) and \(small dog\) show/ | |
| are exactly the same command as far as they go. But the | |
| substitution command remembers what it finds to match the text | |
| between a pair of "\(" and "\)" metacharacters, for use in the | |
| replacement text. Whenever your replacement pattern contains "\1" | |
| the editor replaces that metacharacter with whatever matched the | |
| characters that were between the first pair of "\(" and "\)" | |
| metacharacters. A "\2" in the replacement pattern is removed and | |
| replaced by whatever was matched the characters between the second | |
| pair. And so on -- you can have up to nine pairs in one | |
| substitution command. These metacharacter pairs can even be | |
| nested in the to-be-replaced text; the one that starts first will | |
| be represented by "\1" and so on. So if you extend that second | |
| substitution command above to read: | |
| substitute /^The \(cat\) and \(small dog\) show/My \2-\1 fair | |
| the substitution command will produce a line that begins: | |
| My small dog-cat fair | |
| Or if you type: | |
| substitute :up \(and \)\(over \)\(the sky\):\2\1\2\1\2\3 | |
| then your command will change the first line below to read as the | |
| second line, just beneath it: | |
| up and over the sky | |
| over and over and over the sky | |
| (I used the colon ":" character to separate the parts of the | |
| command, instead of the slash "/" character, solely to make it | |
| more readable for you. There is no danger of the editor confusing | |
| "/" with "\" or "l" (el) with "1" (one) etcetera.) | |
| As the preceding examples show, the "\(" and "\)" are not too | |
| useful with plain-text search patterns; about their only real | |
| value there is when you are searching for something that's | |
| difficult to spell correctly, and don't want to type it into the | |
| replacement pattern with possible spelling errors. (Spelling | |
| errors aren't so dangerous in the to-be-replaced text, because | |
| they only cause the pattern match to fail.) | |
| These metacharacters save the day, though, when you are dealing | |
| with other search metacharacters in searching for text that you | |
| will want to put back in. (Often the only way to specify the | |
| exact spot you want the replacement done is to include some | |
| neighboring text in the search pattern, and tell the editor that | |
| after the neighboring text has been taken out it is to be put back | |
| in right where it was.) Here are three examples of this kind of | |
| substitution: | |
| % substitute :\([Ss]ection\) \([0-9][0-9]*\):\1 No. \2:g | |
| /\([Ss]ection\) \([0-9][0-9]*\)/ substitute ::\1 No. \2 | |
| % substitute ,[Aa]nswer: \([TtFf] \),ANSWER: \1,g | |
| The first of these simply inserts "No." in the middle of phrases | |
| that are section numbers, throughout the document. But the "\(" | |
| and "\)" notation is essential to preserve the section number in | |
| each case, and also to leave unchanged the capitalization or | |
| noncapitalization of the first letter of "section". | |
| The second command does the same thing, but only on the very next | |
| line that has a section number to change. The surprise here is | |
| that I put the "\(" and "\)" in the address pattern to find the | |
| correct line. A line address doesn't use these metacharacters, of | |
| course, but it does not object to them, either. It just ignores | |
| them in its own line search, but does pass them along when a | |
| following substitution command reuses the last search pattern, as | |
| happens in this example. | |
| The third example is useful in editing a list of answers to | |
| exercises. It stops at each answer to a true-or-false question and | |
| capitalizes the entire word "answer". The innovative aspect of | |
| this command is that it finds the letter "T" or "t" or "F" or "f" | |
| following the word "answer", so it will not change the | |
| capitalization where an answer is numerical rather than true or | |
| false. And yet, the letter indicating whether "true" or "false" | |
| is the correct answer is not discarded as a side effect. This is | |
| primarily an example of a change that can be done more simply by | |
| using other metacharacters in the replacement pattern. Those other | |
| metacharacters are described below. | |
| Replacement-Pattern Metacharacters | |
| The string of characters you want to put in via a substitution | |
| command can use its own list of metacharacters. They're entirely | |
| different from the metacharacters used in searching for a pattern | |
| you want to take out of a line. | |
| & | |
| In a replacement pattern, the "&" stands for the entire text | |
| that was to be replaced. Use this when you want to add rather | |
| than replace text. For example, to change "kit" to "kit and | |
| kaboodle" regardless of whether "kit" is capitalized, use: | |
| % substitute /[Kk]it/& and kaboodle/g | |
| If you have magic turned off, you must backslash the "&" to give | |
| it metavalue. With magic on, backslash an "&" to insert it as | |
| a regular character. (See the explanation of set in the | |
| subsequent article). | |
| ~ | |
| The "~" character represents the replacement pattern you used in | |
| your last substitution command. One good use for this is in | |
| correcting various misspellings of a single word: | |
| % substitute /[Ff]anstock clip/Fahnestock clip/g | |
| % substitute /[Ff]ahnstock clip/~/g | |
| % substitute /[Ff]ahnstocke clip/~/g | |
| % substitute /[Ff]annstock clip/~/g | |
| % substitute /[Ff]anestock clip/~/g | |
| % substitute /[Ff]aenstock clip/~/g | |
| If you have magic turned off, you must backslash the "~" to give | |
| it metavalue. With magic on, backslash a "~" to insert it as a | |
| regular character. | |
| \U | |
| A "\U" metacharacter makes all letters following it into | |
| capitals; to the end of the replacement pattern or until another | |
| metacharacter turns it off. Here's how you'd use it to change a | |
| section of your document to all capitals: | |
| 1 , substitute /.*/\U& | |
| \L | |
| A "\L" gives the opposite effect of a "\U"; all following | |
| letters become lower case. You might use this to decapitalize | |
| acronyms: | |
| % substitute /FORTRAN and COBOL/\L&/g | |
| \E | |
| Use "\E" to end the effect of a "\U" or "\L" metacharacter. | |
| Everything following the "\E" has the same mix of capitalization | |
| as originally. For example, to enclose a line of text in curly | |
| braces, and also change just the first word to all capitals: | |
| substitute :\([^ ]*\)\(.*\):{\U\1\E\2} | |
| No "\E" is needed when you switch from "\U" to "\L" in the | |
| middle of a replacement pattern, or vice versa. When either of | |
| those metacharacters appears, it automatically ends the effect | |
| of the other. So if you have a list of book titles, one title | |
| per line, with only the first letters of the words capitalized, | |
| and you want to change those titles to all capitals before the | |
| colon in the middle of each title, and all lower case after it, | |
| just type: | |
| % substitute ,\(.*\):\(.*\),\U\1:\L\2 | |
| \u | |
| This metacharacter capitalizes just the single letter that | |
| immediately follows it. If the character that immediately | |
| follows it is not an alphabet letter, "\u" does nothing. | |
| \l | |
| The same as "\u", except that "\l" makes the immediately | |
| following letter come out as lower case. | |
| One more thing that's important to know about reusing patterns in | |
| substitution commands. When all or part of a text-to-be-replaced | |
| pattern is going to be used as a replacement pattern, or vice | |
| versa, the command reuses the result of the original pattern, | |
| after all the metacharacters have been evaluated in the original | |
| situation. Since the metacharacters in either of those two types | |
| of patterns have no meaning in the other type, it could hardly be | |
| otherwise. | |
| But when the reuse involves a text-to-be-replaced pattern being | |
| used a second time as a text-to-be-replaced pattern, or a | |
| replacement pattern being reused as a replacement pattern, the | |
| command brings along all the original metacharacters and evaluates | |
| them afresh in the new situation. Thus, in either of the cases in | |
| this paragraph, the second use is unlikely to produce exactly the | |
| same results as the first use did. | |
| Now another exercise for you. Suppose that lines 237 through 289 | |
| of a file have unknown capitalization--any line could be all caps, | |
| all lower case, or some mixture. These lines are to be changed so | |
| that the first letter of every word is a capital and all other | |
| letters are lower case. To simplify this, words are separated by | |
| space characters. What is the easy way to do this with one | |
| line-mode substitution command? This exercise depends on | |
| something I did not state directly, so don't feel bad if my | |
| solution is a little simpler than yours. | |
| Other Uses for Substitution | |
| Despite the name, the :substitute command doesn't always take | |
| something out of the line and put something else in its place. | |
| Here's an example that adds text at the start of certain lines | |
| without removing anything: | |
| 537 , 542 substitute /^/WARNING: / | |
| so that text which originally looked like this: | |
| The primary output line carries very high voltage, which does not | |
| immediately dissipate when power to the system is turned off. | |
| Therefore, after turning off the system and disconnecting the | |
| power cord, discharge the primary output line to ground before | |
| servicing the output section. | |
| now looks like this: | |
| WARNING: The primary output line carries very high voltage, | |
| WARNING: which does not immediately dissipate when power to | |
| WARNING: the system is turned off. Therefore, after turning | |
| WARNING: off the system and disconnecting the power cord, | |
| WARNING: discharge the primary output line to ground before | |
| WARNING: servicing the output section. | |
| It's just as practical to pull some text out of lines without | |
| putting anything back in its place. Here are two command lines | |
| that do just that: | |
| % substitute / uh,//g | |
| . , $ substitute / *$ | |
| The latter command removes superfluous spaces at the ends of | |
| lines. It doesn't need the final two slashes because there is no | |
| suffix to be distinguished from a replacement pattern. | |
| At times you might use both the previous principles, to create | |
| :substitute commands that neither subtract nor add any text. | |
| Sound pointless? Here's one such that I sometimes use when I'm | |
| well along in writing one of these tutorials: | |
| % substitute /^$ | |
| Now here's a different kind of exercise for you. I've already | |
| given you the command, above. It obviously makes no change | |
| whatsoever in the file. So why do I run this command? You need a | |
| goodly dose of inspiration to answer this, so don't be embarrassed | |
| if you have to look at my answer to this one. | |
| A Start on Script Writing | |
| Already you know enough about the editor to be able to plan some | |
| fairly complex edits. Here's a short introduction to the art of | |
| writing editing scripts for this editor. | |
| BOTTOM-UP PROGRAMMING. That's usually the best way to build a | |
| complex editor command or command script. That's a programmer's | |
| term that means putting all the little details in separately and | |
| then pulling them all together into a unified whole, rather than | |
| starting with a grand overall plan and forcing the details to fit. | |
| For example, reader R.T. of San Francisco, California asks how to | |
| use the editor to automatically add HTML paragraph tags to each | |
| paragraph of manuscripts. This requires inserting the string | |
| "" at the start of the first line of each paragraph, and | |
| the string "" at the end of the last line. In these | |
| manuscripts, a completely empty line (not even a non-printing | |
| character on it) separates one paragraph from another. | |
| This looks pretty easy. All that seems to be needed is to go to | |
| each empty line, then move up to the preceding line to insert the | |
| end-of-paragraph string and down to the following line to put in | |
| the start-of-paragraph string. But there are flaws in the obvious | |
| command to do this: | |
| global /^$/ - substitute :$:: | ++ substitute /^// | |
| The first problem is that when the editor goes to the empty first | |
| line that commonly begins a file, it will be unable to move up a | |
| line to do the first substitution. No substitution is needed | |
| there, of course, but since the editor doesn't leave that empty | |
| first line, moving down two lines will put it on the second line | |
| of the following paragraph -- definitely the wrong place for a | |
| start-of-paragraph tag. There are several ways to fix this | |
| problem: | |
| Have the editor :mark the empty line before leaving it to execute | |
| (or attempt to execute) the first substitution. Then it can go to | |
| the marked line (which works even if the editor never left it) and | |
| drop down one line to perform the second substitution. | |
| Change the address of that second substitution command from "++" | |
| to "/./" in order to move forward to the next nonempty line, which | |
| will be the first line of the following paragraph whether the | |
| search starts from the empty line or the line above it. | |
| Run two separate :global searches, each of which executes one of | |
| the two substitution commands. | |
| Problem number two is that there may be several empty lines | |
| between two paragraphs, since HTML interpretation is not affected | |
| by them. If the editor is on the first of two or more consecutive | |
| empty lines, the command I first proposed above will perform its | |
| second substitution on the second empty line just below it. When | |
| it moves to the second previously-empty line, it will run the | |
| first substitution co mmand on the empty line it just left. (Yes, | |
| the second line is no longer empty, but it has already been marked | |
| by the :global command before any substitutions are done.) That | |
| is, a stretch of text that initially looked like this: | |
| at this meeting, so be sure to be there! | |
| At next month's meeting we'll hear from the new | |
| and should have been edited to look like this: | |
| at this meeting, so be sure to be there! | |
| At next month's meeting we'll hear from the new | |
| actually turns out like this: | |
| at this meeting, so be sure to be there! | |
| At next month's meeting we'll hear from the new | |
| It may look as though this hazard can be defeated by modifying the | |
| number two solution to the first problem above. That is, the | |
| address for both substitutions will be a search pattern that looks | |
| for a line that already has some text on it. This works properly | |
| when the editor is on the first of two consecutive empty lines. | |
| From the second line, though, it runs its substitution commands on | |
| lines that have already been given their tags, so the sample text | |
| now looks like this: | |
| at this meeting, so be sure to be there! | |
| At next month's meeting we'll hear from the new | |
| COMPLEX CONDITIONALS. What's really needed here is | |
| double-conditional execution. That is, substitution commands must | |
| run on a given line only if both of these conditions are true: | |
| - The line to be substituted is adjacent to the empty line. | |
| - The line to be substituted is not itself empty. | |
| In this case, the editor can handle it. The :global portion of | |
| the command line takes care of the first condition if the | |
| substitution commands' addresses move exactly one line in each | |
| direction from the empty line. (Of the three proposed solutions | |
| to the first difficulty encountered, numbers one and three both do | |
| this much.) To satisfy the second condition, make the | |
| substitution commands remove one character from the existing line | |
| -- and then replace it, of course. This ensures that if there is | |
| no character to remove because the line is empty, the substitution | |
| command will fail on that line and do nothing. | |
| Either the first or third solution can be adapted to satisfy that | |
| second condition. I've used the third solution in the example | |
| commands below, because the technique is easier to follow than it | |
| would be with the first solution: | |
| global /^$/ + substitute /^./&/ | |
| global /^$/ - substitute :.$:&: | |
| Bottom-up techniques can be continued if there are yet other | |
| special needs to be accommodated. Reader R.T. may have headlines | |
| and subheads mixed in with the paragraphs, and may already have | |
| appropriate HTML tags at the beginnings and ends of those heads | |
| and subheads. As an exercise, how would you adapt the commands | |
| just above so they would not add a paragraph tag where any text | |
| already begins or ends with an HTML tag? Hint -- an HTML tag | |
| always begins with a "" character. This is a | |
| very minor change, so you probably will not need to look at my | |
| solution except to confirm your own answer. | |
| A LITTLE TRICKERY. At times a command needs to be supercharged by | |
| way of a far out use of substitution--something perfectly | |
| legitimate, but never intended by the people who wrote this | |
| editor. Here are a few that you may find useful. | |
| You can't make a substitution that extends over more than a single | |
| line--not directly, that is. Any attempt to put a "newline" | |
| character in either the to-be-replaced pattern or the replacement | |
| pattern of a substitution command will fail. But by combining the | |
| global and substitute commands, you can often get the effect of a | |
| substitution that spills over a line ending. | |
| Let's suppose that you have to alter a long document so that all | |
| references to "Acme Distributors" are changed to "Barrett and | |
| Sons". A simple substitution command will make most of these | |
| changes, but it will miss those instances where "Acme" appears at | |
| the end of one line and the next line starts with "Distributors". | |
| A followup pair of substitutions, to replace "Acme" wherever it | |
| appears at the end of a line and to replace "Distributors" when it | |
| starts a line, would wreak havoc--this document also refers to | |
| "Acme Supply Co." and to three other companies whose names end | |
| with "Distributors". | |
| But we can handle this problem nicely with the following two | |
| command strings: | |
| global /Acme$/ + substitute /^Distributors/and Sons | |
| global /^and Sons/ - substitute /Acme$/Barrett | |
| The first command goes to every line that ends with "Acme" and | |
| then moves forward one line--if and only if that next line begins | |
| with "Distributors", it is changed to begin with "and Sons". The | |
| next command reverses the process to change "Acme" to "Barrett", | |
| but only in the right instances. (Note well that the second | |
| command searches for "and Sons", not "Distributors", because the | |
| first command has changed those line-split "Acme Distributors" to | |
| "Acme and Sons".) | |
| Often it is a good strategy to start with a change you definitely | |
| don't want in order to wind up with what you do want. Suppose you | |
| are a technical writer who has just finished writing a number of | |
| lengthy photo captions full of phrases like "the light spot in the | |
| upper righthand corner" and "dark areas near the lower lefthand | |
| edge". Along comes the news that the Art Director has decided to | |
| flop all the photos: print them in mirror-image form. Suddenly, | |
| everything that was on the right is now on the left, and vice | |
| versa. | |
| Your captions will be accurate again if you change every | |
| "lefthand" to read "righthand" and vice versa. But how to do that | |
| without wading through the whole text and making each change | |
| individually? The obvious pair of substitutions will not work: | |
| % substitute /lefthand/righthand/g | |
| % substitute /righthand/lefthand/g | |
| The second command doesn't just change the original instances of | |
| "righthand" to "lefthand"; it also reverses every change your | |
| first command made--now everything is described as being on the | |
| lefthand side. But the following three substitution commands will | |
| do the job nicely. | |
| % substitute /lefthand/QQQQ/g | |
| % substitute /righthand/lefthand/g | |
| % substitute /QQQQ/righthand/g | |
| By making the first command change "lefthand" temporarily to | |
| "QQQQ" (or any other string you know will not be found in your | |
| document), you keep those changes safe from the effect of your | |
| second command. Then, after that second command has finished, the | |
| third command changes those Q strings to what you had wanted in | |
| the first place. | |
| It can even make sense to type in things incorrectly, then change | |
| them to what you want via substitution. When I'm writing | |
| documents in plain ASCII, to be printed without any formatting, I | |
| often use a line across the page to separate major sections of the | |
| document. But where others are satisfied with just a string of | |
| hyphens, or another single character, I pretty things up with | |
| multicharacter dividers like: | |
| -=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | |
| -+--+--+--+--+--+--+--+--+--+- | |
| *~*~*~*~*~*~*~*~*~*~*~*~*~*~*~ | |
| [][][][][][][][][][][][][][][] | |
| Not that I have the patience and concentration to type in | |
| page-wide lines of alternating characters, especially when I would | |
| have to constantly get on and off the shift key, too. No, I just | |
| use my repeat key to fill the line with whatever character will | |
| begin my eventual multicharacter pattern. For those four patterns | |
| above, I would have repeat-keyed in these four lines, respectively: | |
| ------------------------------ | |
| ------------------------------ | |
| ****************************** | |
| [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ | |
| Then I only have to run a simple repeated substitution to get the | |
| line I actually want. Here are the commands I would run on the | |
| four lines above, respectively: | |
| substitute /--/-=/g | |
| substitute /---/-+-/g | |
| substitute /\*\*/*\~/g | |
| substitute /\[\[/[]/g | |
| SEMI-AUTOMATIC SUBSTITUTIONS. At times you'll have to make | |
| changes that are so dependent on human judgment that no | |
| substitution tricks could possibly do exactly what's wanted. In | |
| those cases there are two ways to have the editor partially | |
| automate those changes. | |
| The first is to use a variant form of the substitute command that | |
| presents each potential substitution on your screen, and asks you | |
| to accept or reject it. All you need to do is put a lower-case | |
| "c" at the end of your substitution command, in the same place you | |
| would put a "g" to change every instance on each line, like either | |
| of these examples: | |
| % substitute /^something/something else/c | |
| % substitute /something/something else/gc | |
| The editor will then display the lines where substitutions are to | |
| be made on your screen, one at a time. Each line will have "^" | |
| marks below the text to be removed, like this: | |
| something in the air. The consensus is that | |
| ^^^^^^^^^ | |
| and if there are two or more places on the line where the | |
| substitution could be performed, the line will be displayed on | |
| your screen two or more times, with a different potential | |
| substitution marked each time. After displaying a line on your | |
| screen, the editor will wait for you to type something ending with | |
| a carriage return. If whatever you type begins with a lower-case | |
| "y", the change will be made. If it begins with anything else, | |
| the text will be left as it is. | |
| Even this substitution variant may not give you enough control. | |
| You may need to see more than one line to verify your judgment, or | |
| the text to be put in may vary from one place to another. In | |
| those cases, you can use one of the qualities of the :global | |
| command. This is a simplified version of the technique our | |
| programmer friend Hal (in the first installment of this tutorial) | |
| used to work over the problem points that Lint found in the code | |
| he was updating. | |
| If you are editing in screen mode, as usual, you must start by | |
| typing a capital "Q" to go into line mode. From line mode's colon | |
| prompt, give a command like the following (if you want to make the | |
| same substitution as in our previous examples): | |
| global /something/ visual | |
| This command will bring you in turn to each line in the file that | |
| contains the string "something" and put you in screen-editing mode | |
| there. After you've looked around, and made a substitution if you | |
| think it justified, typing a capital "Q" takes you out of | |
| screen-editing mode and returns you to the global command, which | |
| takes you the next marked line and resumes screen editing. | |
| There is an indirect hazard in leaving screen editing mode, | |
| though. And that brings us to the whole dismal subject of | |
| preventing loss of your changes, or of your entire file, while you | |
| are in the editor. | |
| Don't Lose Your Files | |
| The vi/ex editor is not strong on protecting you from the | |
| consequences of your own mistakes. In part that's just the | |
| natural result of giving you a lot of editing power. But when it | |
| comes to losing all the changes you've made in a file during a | |
| session, or even losing the original file you started with, the | |
| editor could be a lot more responsible without hamstringing your | |
| subtle edits. Still, there are ways you can comfortably protect | |
| yourself from those hazards, and many of those ways I explain | |
| below. | |
| IN EMERGENCIES. Consider one of the editor's safety features that | |
| can accidentally but quite easily turn into a disaster. You may | |
| already know that when you edit with this editor, you are working | |
| on a copy of the file, not the original. Your changes do not | |
| affect the original unless you use the :write command, for which | |
| :w is the shortest abbreviation, or leave the editor in a normal | |
| way. That's a good precaution against making a mistake with this | |
| powerful editor that mangles your file, and then discovering you | |
| have no backup copy. | |
| That copy you are working on lives in a volatile place, though, | |
| where it can easily be erased when the system crashes or your link | |
| into the system goes down. That could cost you all the additions | |
| and changes you'd made in that session with the editor. Your | |
| first line of defense against this is to run the :write command | |
| often--every time you run it, your current edited version replaces | |
| the previous version in the stable permanent-storage area on disk. | |
| And if you don't intend to change the original? Your edited | |
| version is to be a new file, with the original left untouched? | |
| Well, you can use a modified form of writing the file, by typing | |
| :write nufile where "nufile" is whatever you want the name of the | |
| edited file to be. (That can be a path name, in case you don't | |
| want to put the new file in your current directory.) This will | |
| write the revised version only to the new file (creating the file | |
| if necessary), and leave the original file untouched. | |
| That method of preserving the original file is dangerous, though. | |
| If you forget even once to add the filename to the :write command, | |
| your original file is wiped off the disk. That's why this | |
| technique is best reserved for occasions where you do want to | |
| change the original file, but also want a copy of some partially | |
| edited stage of the file saved elsewhere. When you get the file | |
| to the state you want to save as a copy, run both of these | |
| commands: | |
| write nufile | |
| write | |
| and then go back to editing and writing to the file as usual. | |
| The sane way to protect your original file from any changes is to | |
| start your editing with a :file nufile command, for which :f | |
| nufile is the shortest abbreviation. From that point on, the | |
| editor considers "nufile" to be the name of the file you are | |
| working on, and writes go automatically to that filename. | |
| (Incidentally, if you ever forget whether you have changed the | |
| file name, or what you've changed it to, a :file command by | |
| itself, without a file name, will tell you what the editor thinks | |
| the file's current name is, among other interesting facts.) | |
| CRASHES WILL HAPPEN. Still, a crash may catch you by surprise, | |
| with a lot of additions and changes that you have not written to | |
| any file. To protect against this, the editor always attempts to | |
| save your current working copy of the file when a crash is | |
| imminent. You can even launch an emergency save yourself when you | |
| face a sticky situation, such as being unable to do a normal write | |
| because it would exceed your filespace quota. Just type a | |
| :preserve command (or its :pre abbreviation) and the working copy | |
| is normally preserved. There are a few gotchas to watch for, | |
| though. | |
| The preservation function puts the saved copy in a specific | |
| directory, and it will fail if that directory does not exist or is | |
| not writable. (The path name of that directory varies between | |
| versions of the editor, although /var/preserve seems to be a | |
| common choice on modern Unix systems.) To check writability, run | |
| a :preserve command from a short file, as a test. If the result | |
| is a message something like this: | |
| Can't open /var/preserve | |
| Preserve failed! | |
| there is a problem you will have to take up with your system | |
| administrator. (To speed up that discussion, bring along the path | |
| name of the directory that couldn't be opened.) If the message | |
| reads like this: | |
| File preserved. | |
| so far, so good. The next question is whether the editor has | |
| preserved an accurate copy or a pile of garbage--some editor | |
| implementations are broken in this area. To check this, recover | |
| the file you've just preserved. | |
| RESCUING SAVED FILES. There are two ways to recover a rescued | |
| working copy, whether it was saved in a crash or because you used | |
| the :preserve command. Usually you recover it from your shell | |
| command line, by entering the editor with a "-r" flag: | |
| vi -r novel.chap3 | |
| vi -r | |
| The first of these commands puts you in the editor, with the | |
| latest rescued copy of your file "novel.chap3" before you. The | |
| latter command doesn't put you in the editor at all; it displays a | |
| list of all files that have been emergency-saved and then returns | |
| you to the shell command line. This list is useful when, say, the | |
| system crashed while you were editing a file that you hadn't given | |
| a name. (Yes, you can enter the editor without giving a filename | |
| to edit; the editor will simply bring up an empty buffer and | |
| assume you will give it a name later.) In this case the | |
| preservation process will give the file a name, and you must know | |
| this name to recover it. | |
| I said that the first command would bring up the latest rescued | |
| copy of the file you named. If the system has been staggering for | |
| a while, there may be more than one occasion when either you or | |
| the system caused the editor to preserve the working copy of that | |
| file. If the latest version is not the best copy, you can discard | |
| it and pull up the next most recent version, without leaving the | |
| editor. Just give a :recover command (or its :rec abbreviation) to | |
| have the current rescued version discarded and replaced by the | |
| next-latest of the saved copies. (When you're already in the | |
| editor, there's no need to give the name of the file to recover if | |
| it is the same as that of the file you're editing at the time. | |
| The editor assumes the current filename unless you give another | |
| name as an argument following the :recover command.) If this | |
| isn't the best copy either, you can continue this process. | |
| When you've recovered a file either way, look it over. If the | |
| editor version you're using has a broken preservation function, | |
| you'll only find garbage characters or a display like this: | |
| LOST | |
| LOST | |
| LOST | |
| LOST | |
| LOST | |
| If that be the case, the file you preserved is hopelessly lost and | |
| you'd better have a talk with your system administrator about | |
| getting a better version of the editor. But if what you see looks | |
| like what you had, then all you have to do is write the copy | |
| you've recovered to a file somewhere--the preserved copy was | |
| erased when you used one of the recovery commands, so it can't be | |
| recovered that way again. | |
| And that brings up the last gotcha. You may believe that any of | |
| the three commands ZZ or :x or :wq will check whether the working | |
| copy needs to be written to the file, write it only if necessary, | |
| and then quit the editor. Actually, the last of the three, the | |
| :wq command, always writes to the file regardless, and is the only | |
| one you should use. | |
| The first two attempt some checking, but their checks are not very | |
| complete. In particular, they and the :quit command often check | |
| for nothing more than the flag that is set when an editing change | |
| is made to the current working copy and unset when the working | |
| copy is written to the file. You are courting disaster if you | |
| ever use the ZZ or :x commands, or if you use :quit carelessly. | |
| The gotcha in the case of a recovered file is that pulling a new | |
| file into the buffer, whether normally or by recovering an | |
| emergency-saved copy, is not an editing change. If your version | |
| of the editor has a weak version of ZZ or :x then its casual check | |
| will show no reason to write the file, and all your carefully | |
| recovered work will be lost for good when the command closes the | |
| editor without writing the file. So always use :wq or separate | |
| :write and :quit commands to end an editing session. | |
| A FEW MORE HAZARDS AND SOLUTIONS. Worse yet can befall you. You | |
| may accidentally lose both your own editing changes and the | |
| original file you were working from. | |
| Suppose one of your global editing commands went astray and | |
| trashed your working copy of the file, but didn't happen to affect | |
| the part that is on your screen. If you then wrote the working | |
| copy to the file, the garbage replaced your original file | |
| contents. Oh, misery! And with any but the smallest file, it's | |
| not practical to look over the working copy carefully before each | |
| :write command. | |
| Or perhaps you did discover the disaster before you wrote the | |
| working copy to the file. Seeing that undoing the errors was not | |
| feasible, you decided either to run an :edit! command to bring up | |
| a fresh copy of the original file, or a :quit! to leave the | |
| editor. In either case, the "!" at the end of the command tells | |
| the editor to ignore the garbage changes that have not been | |
| written to the file. | |
| But since you were not creating an editor script here, you | |
| probably typed the short form of your command, either :e! or :q!. | |
| At least you tried to. Perilously placed between the "e" and "q" | |
| keys on a standard keyboard is the "w" key. If you accidentally | |
| typed :w! instead of what you intended, you told the editor to | |
| overwrite the original file with that trashed version, and ignore | |
| any anti-write protections you may have set. Oh, misery cubed! | |
| You are not lost yet, though, if you have been editing along in | |
| screen mode all the while. At any time you can type a short | |
| sequence to put the working copy back the way it was when you | |
| started this editing session. Then you only need to write the | |
| working copy to the file to expunge the trash there. | |
| Start by typing Q to leave screen mode and drop back to line mode. | |
| Now, line mode has an undo command that works the way the screen | |
| mode u command does, but on line mode commands. That is, it | |
| reverses the effect of the latest line mode command that changed | |
| (or may have changed) the working copy. One line mode command | |
| that may well change the working copy is visual, of course. And -- | |
| surprise -- when you typed vi novel.chap3 from your shell prompt | |
| to enter the editor, your shell actually launched ex (for which vi | |
| is just an alias) and the editor gave itself an initial visual | |
| command to boost you into screen mode. | |
| So all the time you've been editing, the editor has been holding a | |
| complete copy of the original file, in case you go back to line | |
| mode and want to reverse the effect of that initial visual | |
| command. (That's one reason the editor's buffer takes up so much | |
| more room than you'd expect in swap space.) If you want to see | |
| the complete command sequence to restore the original working copy | |
| and return to visual mode, using shortest abbreviations and | |
| showing carriage returns as new lines, here it is: | |
| Qu | |
| w | |
| vi | |
| One last hazard, which may seem childish to experienced Unix users | |
| but trips up many a refugee from single user systems. Unless | |
| you're on one of those rare Unix implementations that offers file | |
| locking, there is little to prevent another user on the system | |
| from editing the same file at the same time as you do. | |
| You will each be editing on a separate working copy, so there will | |
| be nothing to tell you that someone else is also editing the same | |
| file. But each time you write your changed version to the file, | |
| you will wipe out whatever changes the other user has already | |
| written to file, and vice versa. The ultimate victor in this | |
| unknowing war will be the user who finishes editing last. The | |
| other user can come back an hour later and find no indication that | |
| he/she ever touched the file. | |
| There's no real technical solution to this danger. You'll just | |
| have to coordinate carefully with other users on files that more | |
| than one of you may have occasion to edit. | |
| Reader Feedback | |
| One of our readers raised a significant point about this | |
| technique; important enough to deserve a reply published in this | |
| article. | |
| Dear Walter... | |
| In your tutorial you write that you can use the command | |
| global/XXX/visual | |
| to search for the pattern "XXX" and edit/move around (remember, | |
| Hal needed this command to edit the linted spaghetti-code...) | |
| But there's one problem: suppose I found, after the 10th XXX of | |
| 100, that I do not want to view the remaining 90 occurences. It | |
| works as long as I don't type 'Q'. But now I want to view/edit | |
| the code where my lint report is something like "illegal", I have | |
| to type Q and then global/illegal/visual. | |
| And now there's the problem: typing Q doesn't prompt for a new | |
| input, it moves to the 11th occurence of "XXX". | |
| Do you know my problem? Is there a way to stop vi moving on with | |
| the global command after typing Q? | |
| Thanks a lot in advance! | |
| Chris... | |
| As Chris clearly realizes, ordinarily there is no problem with | |
| omitting the remaining 90 stops. Each time this command puts you | |
| into visual mode somewhere in the file, you are not restricted to | |
| fixing one limited problem. You may move anywhere in the file, | |
| edit whatever you like, and keep doing this as long as you please. | |
| When you finally finish all the edits you've decided to do, you | |
| can write the file and quit the editor in your usual way--the | |
| suspended global command will silently disappear at this point. | |
| But going into a second string of visual mode stops from a new | |
| global command, as Chris wants to do, requires finesse. | |
| Obviously, it's not possible to use the Q command to return to the | |
| line-mode command prompt until every one of those 100 lines has | |
| been visited and the first global has terminated. | |
| The best way out of this predicament starts with writing your | |
| changes to the file. Then, instead of typing Q type an :edit | |
| command. This brings up a fresh copy of the file you are editing, | |
| but since you've just done a write, the fresh copy is identical to | |
| the one you've been working on. Because you haven't left the | |
| editor, most of the state is saved--contents of the named buffers, | |
| any maps and/or abbreviations, values of the set command options. | |
| You do wipe out a few small items like the contents of the unnamed | |
| buffer--and, of course, that suspended global command. | |
| Now you can use the Q command to go into line mode, then run that | |
| second global command. | |
| Solutions | |
| In The Next Installment | |
| In this tutorial to date, you've undoubtedly seen some aspects of | |
| the editor that you wish had been designed differently. The good | |
| news is that many of these features are yours to change at | |
| will--without hacking up the source code and recompiling. | |
| In Part 5 of this tutorial, I'll elucidate the editor's built-in | |
| facilities for setting up your own editing environment, and the | |
| many factors you can modify this way. | |
| Part 5: Take Control of Your Editing Environment | |
| Back to the index |