The text I want to process is like (after sorting regardless of the first column: sort/^[^|]*/):

236|c this is character ace|end but not the same
i24|f string f is different|this is the end 
235|this is character c|end
235|this is character c|end but 
235|this is character c|end but not the same
235|this is character c|end but not the same
422|this is string aa|end of the line
312|this is string af|end but maybe not the same
423|this is string af|end but not the same
123|this is string af|end of line one
422|this is string af|end of the line
422|this is string a|end of the line
51k|this is string e|end but not the same
13d|this is string e|end of the line
341|z string b is different|end of the line but not the same

I want to move all lines duplicated in the middle column to the end of the file. That's, all unique lines appear in the beginning of the file, followed by the duplicated(only duplicated in the middle column) lines.

I have tried

  1. g/\v\w{3}\|([^|]*)\|[^|]+$(\r?\n\w{3}\|\1\|[^|]+$)+/m$
    This will end with the first occurrence of the duplicated lines not moved, which works as expected considering I learn this from the method to delete the duplicated lines and leave only the first one.

  2. %s:\v^\w{3}\|(.*)\|[^\n]+(\n\w{3}\|\1[^\n]+)+::cg
    This time I can get only the unique lines but I lose what I want.

I thought both two approaches can lead to the result, but the questions left respectively are:

  1. How to move the matched lines in one go, or move the first one after this step; and
  2. How to delete all lines in file2 which are also in file2(file1 is the original text and file2 contains only unique lines).

Or moving only the unique lines can also reach the goal.

Am I in the right direction? Or any vim-ish/straightforward methods? Thanks.

1 Answer 1


Here is one solution. It first marks all duplicate lines (by appending a string to them). As your pattern starts a match on all but the last duplicate line, we need another pass with a modified pattern to also capture the last line. Then move the marked lines to the end of the buffer, and remove the mark(s) from them.

:let @a = ''
:%s/\v^\w{3}\|(.*)\|[^\n]+\ze(\n\w{3}\|\1[^\n]+)+/& SELECT/e
:%s/\v^\w{3}\|(.*)\|[^\n]+(\n\zs\w{3}\|\1[^\n]+)+/& SELECT/e
:global/ SELECT$/move $
:%s/\%( SELECT\)\+$//e
  • A clever solution. Sep 29, 2016 at 1:56
  • Hi, could you please recommend a tutorial or document for the options & and e of substitute command? Sep 30, 2016 at 1:44
  • e is one of the :help :s_flags, and & is documented at :help s/\&. You would typically start at :help :substitute, and read on. Sep 30, 2016 at 8:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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