Grouping Vim Regex duplicate lines

I have a log file similar to this:

12 adsflljl 12 hgfahld 12 ash;al 13 a;jfda 13 asldfj 15 ;aljdf 16 a;dlfj 19 adads 19 adfasf 20 aaaadsf 

And I would like to β€œgroup” them as one of these two:

 12 adsfllj, 12 hgfahld, 12 ash;al 13 a;jfda, 13 asldfj 15 ;aljdf 16 a;dlfj 19 adads, 19 adfasf 20 aaaadsf 

Or

 12 adsfllj, hgfahld, ash;al 13 a;jfda, asldfj 15 ;aljdf 16 a;dlfj 19 adads, adfasf 20 aaaadsf 

And I'm completely stuck. And if vim doesn't do this, I have sed and awk and bash too. I just don't want to write a bash script, I want to increase the value of regex-fu

+4
source share
5 answers

I would just use awk:

 awk ' { sep = val[$1] ? ", " : "" val[$1] = val[$1] sep $2 } END {for (v in val) print v, val[v]} ' log.file | sort > new.file 
+5
source

In Vim, you can use:

 :%s/\(\(\d\+\) .*\)\n\2/\1, \2/g 

which means: if a group of numbers matches after a new line, delete the new line and place a comma instead. If you are not familiar with them, \1 and \2 are backlinks.

Unfortunately, this only combines the two events at a time, so you will have to run it several times before reaching your goal.

EDIT: One way to do this at a time would be to loop and exploit the fact that as soon as the file no longer matches, an error is thrown. The error, however, is a bit annoying, but I could not do better with one layer:

 :while 1 | :%s/\(\(\d\+\) .*\)\n\2/\1, \2/g | :endwhile 
+6
source

In Vim, I would use the command

 :g/^\d\+/y| if+@ "==getline(line('.')-1)|s//,/|-j! 

if it is guaranteed that the first column always contains digital identifiers.

Otherwise, I would modify this if condition as follows.

 :g/^\S\+/y|if matchstr(@",@/)==matchstr(getline(line('.')-1),@/)|s//,/|-j! 
+2
source

Another way to do this is with a macro this time (I advise you to use a different solution, it just shows that there are many ways to do this):

gg:%s/$/, enter qa0V? ctrl-r ctrl-w \>\&^ enter Jjq100@a :%s/.$// return

explanation:

  • gg => go to beginning of file
  • :%s/$/, => add a comma to each line
  • qa => start writing a macro to register a
  • 0V => go to the first column and start line selection
  • ? => look back (you must have set wrapscan )
    • ctrl-r ctrl-w inserts a word under the cursor.
    • \> provides word completion
    • \&^ matches patterns at the beginning of a line. You cannot put ^ at the beginning of the pattern, because if incsearch set, then as soon as you type ^ , then ctrl-r ctrl-w will print the word under the cursor, which will move to the previous line.
  • J concatenates all lines from visual highlighting with spaces.
  • J will go to the next line
  • q will stop macro recording
  • 100@a will play the macro 100 times.
  • :%s/.$// removes commas.
+1
source

I don’t think it’s nice to use regular expressions here. The same thing you can find in the @glenn jackman solution written in vimscript will be as follows:

 function JoinLog() let d={} g/\v^\S+\s/let [ds, k, t; dl]=matchlist(getline('.'), '\v^(\S+)\s+(.*)') | \let d[k]=get(d, k, [])+[t] %delete _ call setline(1, map(sort(keys(d)), 'v:val." ".join(d[v:val], ", ")')) endfunction 

You can keep order instead of sorting:

 function JoinLog() let d={} let ordered=[] g/\v^\S+\s/let [ds, k, t; dl]=matchlist(getline('.'), '\v^(\S+)\s+(.*)') | \if has_key(d, k) | let d[k]+=[t] | \else | let ordered+=[k] | let d[k]=[t] | \endif %delete _ call setline(1, map(copy(ordered), 'v:val." ".join(d[v:val], ", ")')) endfunction 
0
source

All Articles