0

This top line shows how it needs to be formatted, and the other 3 lines show how it currently is formatted. I need the bottom three lines to look like the top line.

So, I need to essentially cut this part XJ.LN34..BHE.M. and place it right before this part SAC at the end of the line. But each line will have different characters that need to be cut and pasted, but they will all be 15 characters long:

SAC/2014.250.144316.XJ.LN16..BH1.M.SAC
SAC/XJ.LN34..BHE.M.2013.361.094720.SAC
SAC/XJ.LL21..BHN.M.2014.250.144316.SAC
SAC/XJ.MW44..BHZ.M.2014.250.144316.SAC

Is there a way to do this for all the lines at once? I have thousands of lines that I need to reformat and I cannot fathom cutting and pasting each line one by one.

Thanks for your help!!

Note. I am using terminal on my mac in c-shell (csh) and I am using vi to open up my file.

3
  • Welcome to Vi and Vim!
    – filbranden
    Apr 29 at 3:51
  • 2
    CTRL+V lets you select a "box". You can select the area you want, cut it (d) and paste it right before the ending .SAC (with p).
    – Shahbaz
    Apr 29 at 4:17
  • @Shahbaz Yeah Visual block is a great solution here! You should post that as an answer.
    – filbranden
    Apr 29 at 4:23
2

If the task is to move 15 characters after the leading SAC/ to immediately before the .SAC at the end of the line I would use a substitute command:

:%s/^\(SAC\/\)\(.*\{15\}\)\(.*\)\(\.SAC\)$/\1\3\2\4/

The search pattern captures the leading SAC/, 15 random characters, the following characters up to the trailing .SAC, and the trailing .SAC, each in its own group. The replacement string just restores the trailing and leading texts ands swaps the 15 stored characters with the following characters.

As @filbranden has noted in his comment the need for backslashes can be reduced and the search pattern can be simplified by putting a \v for "very magic" at the start of the search pattern. With \zs and \ze inside the search pattern the replacement will only operate on the matched text between these two atoms.

Additionally the character that is used to limit search pattern and replacement text can be set to (almost any) non-letter and non-digit. This can be useful if the search pattern or replacement text contains some forward slashes which normally would have to be escaped with backslashes. In your example there is only one forward slash, but let's use all these optimizations. Now the command will look like this:

:%s!^\vSAC/\zs(.*{15})(.*)\ze\.SAC$!\2\1!

The pattern still searches for a leading SAC/ and a trailing .SAC, but they will not be affected by the replacement text. The replacement text will only swap the 15 characters and the remaining character between them.

2
  • 1
    Your regexp might be cleaner if you use \v for "very magic" mode, then you don't need to backslash escape all those groups... You can also use \zs and \ze to avoid having to create capture groups for the beginning and ending which will stay in place: :%s/\v^SAC\/\zs(.{15})(.*)\ze\.SAC$/\2\1/
    – filbranden
    Apr 29 at 17:02
  • 1
    @filbranden Thank you for the tips. I have included them in my answer. Apr 30 at 6:37
1

Vim has quite a few commands to repeat actions. For your particular situation, I'd recommend using :normal with a range, which will perform a sequence of Normal mode commands for every line in the range.

First you need to come up with the sequence of Normal mode commands that will implement your operation. For example, if you want to always delete 15 characters starting at column 4, you can use 4l (move right 4 characters, from the start of the line), followed by d15l (delete the next 15 characters.)

Then the last part is putting the deleted text back before the SAC at the end. Let's say for that part you decide to use $ to move to the end of the line, then b to move to the beginning of the word under the cursor (moving to the beginning of SAC), followed by P to put before the character under the cursor position.

So now you have a sequence 4ld15l$bP that you can repeat on every line to implement your operation.

If you want to repeat it on every line of the current buffer, you can use:

:%normal 4ld15l$bP

If you want to only apply it to some of the lines in the current buffer, then pass it a different range to select the lines. For example, you can use a Visual block (start selecting with V), then with the lines selected press :, which will show you :'<,'>, a range that corresponds to the most recent Visual selection (the lines you just highlighted.)

You can then complete the command:

:'<,'>norm 4ld15l$bP

Vim will perform that sequence of edits on each line of the range.

If you have a more complex sequence of commands, that perhaps might not be very convenient to type in the command-line, you could then record a macro and use that macro to pass it to :normal. See :help recording for details on recording macros.

For example, if you use qa to start recording a macro, then perform your edit on a line, then q to stop recording, you can then use :%normal @a to repeat the sequence of operations in your @a macro on every line of the buffer. If you modified the first line while recording the macro, you can either undo the changes before performing the global :%normal @a (which will reapply it to the first line as well), or you can pass a range that will skip the first line, such as :2,$normal @a.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.