2

In order to parse certain log file and make a plot of sequential numbers, I need to filter out the lines with numbers which appearing lower than in the previous lines.

For example, given the list of lines with integers:

62
116
33
20
61
67
89
82
15
109
73
101
115
120
41
103
94
122

Note: File can be generated by hexdump -n100 -e '1/1 "%d" "\n"' /dev/urandom | grep -v ^-.

I'd like to filter out (remove) the numbers that way, so all of them are sequential (so they look kind of sorted).

Ideally if the command will take into the account all the previous lines.

Here is my expected results:

62
116
120
122

If the command won't filter correctly after first time, I can always re-run it couple of times.

Here is my attempt based on this post:

:g/^/ if getline(line('.')-1) < getline('.') | delete | endif

but it removes all the lines instead.

How I should correct my command? Or what other approach I could use?

2 Answers 2

3

Edit:

Actually, you could do it with a single global command:

g/^/if str2nr(getline('.')) <= str2nr(getline(line('.')-1)) | d_ | endif

Old answer:

Maybe you could try these 2 global commands:

:g/^/if str2nr(getline('.')) <= str2nr(getline(search('[^x]$', 'bnW'))) | s/$/x/ | endif
:g/x$/d_

The first one should append an x at the end of any line which breaks the progression of the numbers to mark it for future deletion. While the second one should delete all the marked lines.


The purpose of str2nr() is to convert the strings into numbers. It matters, among other things, to prevent Vim from performing an octal conversion if the number begins with a 0.

As for search():

search('[^x]$', 'bnW')

Its purpose is to find the previous line which doesn't end with an x, and return its address so that getline() can grab its text contents.

If you're sure that your lines contain only digits, you could modify the invocation of search() like this:

search('^\d\+$', 'bnW')
                  │││    
                  ││└ don't wrap around the end/beginning of the file
                  │└ don't move the cursor
                  └ look backward

If you think your text may originally contain some xs, then you could use another less frequent character, such as C-a:

:g/^/if str2nr(getline('.')) <= str2nr(getline(search('^\d\+$', 'bnW'))) | s/$/\="\<c-a>"/ | endif
:g/\%x01$/d_
3

This feels like a job for awk:

$ awk '$1 > x {x = $1;print}' in_file.txt

Or using filter, :[range]!, inside of Vim:

:%!awk '$1 > x {x = $1;print}'

For more help, see:

:h :range!
:h :range
1
  • 1
    Works great! It is also fairly easy to modify when the numbers are in the different column.
    – kenorb
    Commented Feb 14, 2018 at 15:47

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.