Initial commit
This commit is contained in:
commit
55f5050340
54
README.md
Normal file
54
README.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# lsseq
|
||||||
|
|
||||||
|
A simple filter utility for viewing long listings of text that have
|
||||||
|
either repeating lines or sequential lines. Usage is similar to "uniq",
|
||||||
|
except that in addition to condensing duplicates, it condenses runs
|
||||||
|
of incrementing or decrementing numbered lines.
|
||||||
|
|
||||||
|
In order to be regarded as a run, lines must contain a single block of
|
||||||
|
decimal numerals (with or without zero-padding), which increase by one
|
||||||
|
step on each line. It does not matter if there are additional number blocks
|
||||||
|
that don't change, and the changing number can appear anywhere in the line.
|
||||||
|
|
||||||
|
Although there is nothing about the program that requires it to be used
|
||||||
|
on file listings, it was created to quickly asses PNG or EXR streams
|
||||||
|
generated by Blender rendering processes.
|
||||||
|
|
||||||
|
Currently has no switches or controls, and very few features.
|
||||||
|
|
||||||
|
I also created it as a learning project for **Eclipse/PyDev** and integration
|
||||||
|
with **Github**, so this is more of a learning project, although it is kind
|
||||||
|
of handy.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
$ ls my_pngstream*.png | lsseq
|
||||||
|
|
||||||
|
If the listing looks like this:
|
||||||
|
|
||||||
|
my_pngstream-f00001.png
|
||||||
|
my_pngstream-f00002.png
|
||||||
|
my_pngstream-f00003.png
|
||||||
|
my_pngstream-f00004.png
|
||||||
|
my_pngstream-f00005.png
|
||||||
|
my_pngstream-f00010.png
|
||||||
|
my_pngstream-f00012.png
|
||||||
|
|
||||||
|
The output will look like this:
|
||||||
|
|
||||||
|
my_pngstream-f00001.png
|
||||||
|
... (5) +
|
||||||
|
my_pngstream-f00005.png
|
||||||
|
|
||||||
|
my_pngstream-f00010.png
|
||||||
|
|
||||||
|
my_pngstream-f00012.png
|
||||||
|
|
||||||
|
For very long runs, this will be MUCH more compact and readable.
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
Would like to add option parsing and some options for ignoring parts of
|
||||||
|
the input lines. This would be handy for use with "ls -l" for example,
|
||||||
|
to ignore the changing filesize and data numbers, which would currently
|
||||||
|
foul the run-detection.
|
200
lsseq.py
Executable file
200
lsseq.py
Executable file
@ -0,0 +1,200 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# lsseq
|
||||||
|
"""
|
||||||
|
Filter utility to condense long lists including identical or sequential lines
|
||||||
|
of text (such as the names of files in a PNG stream).
|
||||||
|
"""
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
def main(inp=sys.stdin):
|
||||||
|
"""
|
||||||
|
Given a list of strings or lines from a stream, return a reduced
|
||||||
|
list showing strings of duplicated lines or lines in an incrementing
|
||||||
|
or decrementing sequence (like a PNG-stream):
|
||||||
|
|
||||||
|
>>> sample = '''\
|
||||||
|
... S1E01-PC-1-Cam1-f00001.png
|
||||||
|
... S1E01-PC-1-Cam1-f00002.png
|
||||||
|
... S1E01-PC-1-Cam1-f00103.png
|
||||||
|
... S1E01-PC-1-Cam1-f00103.png
|
||||||
|
... S1E01-PC-1-Cam1-f00103.png
|
||||||
|
... S1E01-PC-1-Cam1-f00104.png
|
||||||
|
... S1E01-PC-1-Cam1-f00105.png
|
||||||
|
... S1E01-PC-1-Cam1-f00106.png
|
||||||
|
... S1E01-PC-1-Cam1-f00107.png
|
||||||
|
... S1E01-PC-1-Cam1-f00108.png
|
||||||
|
... S1E01-PC-1-Cam1-f02000.png
|
||||||
|
... S1E01-PC-1-Cam1-f02300.png
|
||||||
|
... S1E01-PC-1-Cam1-f02400.png
|
||||||
|
... S1E01-PC-1-Cam1-f02399.png
|
||||||
|
... S1E01-PC-1-Cam1-f02398.png
|
||||||
|
... S1E01-PC-1-Cam1-f02397.png
|
||||||
|
... S1E01-PC-1-Cam1-f02396.png
|
||||||
|
... S1E01-PC-1-Cam1-f03555.png
|
||||||
|
... S1E01-PC-1-Cam2-f03333.png
|
||||||
|
... S1E01-PC-1-Cam2-f03440.png
|
||||||
|
... S1E01-PC-1-Cam2-f03441.png
|
||||||
|
... '''
|
||||||
|
>>> print(main(sample.split('\n')))
|
||||||
|
S1E01-PC-1-Cam1-f00001.png
|
||||||
|
S1E01-PC-1-Cam1-f00002.png
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam1-f00103.png
|
||||||
|
x 3
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam1-f00104.png
|
||||||
|
... (5) +
|
||||||
|
S1E01-PC-1-Cam1-f00108.png
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam1-f02000.png
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam1-f02300.png
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam1-f02400.png
|
||||||
|
... (5) -
|
||||||
|
S1E01-PC-1-Cam1-f02396.png
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam1-f03555.png
|
||||||
|
S1E01-PC-1-Cam2-f03333.png
|
||||||
|
|
||||||
|
S1E01-PC-1-Cam2-f03440.png
|
||||||
|
S1E01-PC-1-Cam2-f03441.png
|
||||||
|
|
||||||
|
>>>
|
||||||
|
"""
|
||||||
|
listing=[]
|
||||||
|
prev = None
|
||||||
|
dup_run = 0
|
||||||
|
inc_run = 0
|
||||||
|
dec_run = 0
|
||||||
|
#for line in inp:
|
||||||
|
inp_iter = iter(inp)
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
line = inp_iter.next()
|
||||||
|
except StopIteration:
|
||||||
|
line = None
|
||||||
|
|
||||||
|
if prev is None:
|
||||||
|
# first line
|
||||||
|
prev = line
|
||||||
|
base = line
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line:
|
||||||
|
comparison = compare_lines(prev, line)
|
||||||
|
else:
|
||||||
|
comparison = '!='
|
||||||
|
|
||||||
|
if comparison == '==':
|
||||||
|
dup_run += 1
|
||||||
|
elif comparison == '+1' and not dup_run and not dec_run:
|
||||||
|
inc_run += 1
|
||||||
|
elif comparison == '-1' and not dup_run and not inc_run:
|
||||||
|
dec_run += 1
|
||||||
|
else:
|
||||||
|
listing.append('')
|
||||||
|
listing.append(base)
|
||||||
|
if dup_run:
|
||||||
|
# Report # of duplicates and reset
|
||||||
|
listing.append(' x {0:d}'.format(dup_run+1))
|
||||||
|
|
||||||
|
elif inc_run==1 or dec_run==1:
|
||||||
|
# If a listing is only 2 lines, then just list them
|
||||||
|
listing.append(prev)
|
||||||
|
|
||||||
|
elif inc_run>1:
|
||||||
|
# Show range of increment run
|
||||||
|
listing.append(' ... ({0:d}) +'.format(inc_run+1))
|
||||||
|
listing.append(prev)
|
||||||
|
|
||||||
|
elif dec_run>1:
|
||||||
|
# Show range of decrement run
|
||||||
|
listing.append(' ... ({0:d}) -'.format(dec_run+1))
|
||||||
|
listing.append(prev)
|
||||||
|
else:
|
||||||
|
# Next file is unmatched ('!=') to previous
|
||||||
|
pass
|
||||||
|
dup_run = 0
|
||||||
|
inc_run = 0
|
||||||
|
dec_run = 0
|
||||||
|
base = line
|
||||||
|
if not line:
|
||||||
|
# final iteration
|
||||||
|
break
|
||||||
|
prev=line
|
||||||
|
return('\n'.join(listing))
|
||||||
|
|
||||||
|
|
||||||
|
numfield = re.compile(r'(^\d+)')
|
||||||
|
othfield = re.compile(r'(^\D+)')
|
||||||
|
def breakdown(s):
|
||||||
|
"""
|
||||||
|
Break a string down into numerical fields delimited by non-numerical fields:
|
||||||
|
>>> breakdown('df39-330-spam-51.bar')
|
||||||
|
[('a', 'df'), ('0', '39'), ('a', '-'), ('0', '330'), ('a', '-spam-'), ('0', '51'), ('a', '.bar')]
|
||||||
|
>>>
|
||||||
|
"""
|
||||||
|
fields = []
|
||||||
|
while (s):
|
||||||
|
if numfield.match(s):
|
||||||
|
fields.append(('0', numfield.match(s).groups()[0]))
|
||||||
|
else:
|
||||||
|
fields.append(('a', othfield.match(s).groups()[0]))
|
||||||
|
s = s[len(fields[-1][1]):]
|
||||||
|
return fields
|
||||||
|
|
||||||
|
def compare_lines(l1, l2):
|
||||||
|
"""
|
||||||
|
Compare two lines, returning '!=', '==', '+1', '-1', indicating
|
||||||
|
different, same, incremented by one, or decremented by one:
|
||||||
|
|
||||||
|
>>> compare_lines('my_line2439_3349.png', 'my_line2439_3350.png')
|
||||||
|
'+1'
|
||||||
|
>>> compare_lines('my_line2439_3349.png', 'my_line2439_3348.png')
|
||||||
|
'-1'
|
||||||
|
>>> compare_lines('my_line2439_3349.png', 'my_line2439_8304.png')
|
||||||
|
'!='
|
||||||
|
>>> compare_lines('my_line2439_3349.png', 'spam_8304.png')
|
||||||
|
'!='
|
||||||
|
>>> compare_lines('my_line2439_3349.png', 'my_line2439_3349.png')
|
||||||
|
'=='
|
||||||
|
>>>
|
||||||
|
"""
|
||||||
|
zl = zip(breakdown(l1), breakdown(l2))
|
||||||
|
if all(f1==f2 for (f1,f2) in zl):
|
||||||
|
return '=='
|
||||||
|
|
||||||
|
# Check for ONE field incrementing or decrementing by 1:
|
||||||
|
comparison = [compare_fields(f1,f2) for (f1,f2) in zl]
|
||||||
|
if len([c for c in comparison if c=='+1']) == 1:
|
||||||
|
return '+1'
|
||||||
|
if len([c for c in comparison if c=='-1']) == 1:
|
||||||
|
return '-1'
|
||||||
|
|
||||||
|
#Otherwise, it doesn't match at all
|
||||||
|
return '!='
|
||||||
|
|
||||||
|
|
||||||
|
def compare_fields(f1, f2):
|
||||||
|
"""
|
||||||
|
Compare fields for equality ('=='), incremented by 1 ('+1'), decremented by 1 ('-1'),
|
||||||
|
or other inequality ('!='):
|
||||||
|
|
||||||
|
>>> compare_fields(('0','00345'), ('0','00346'))
|
||||||
|
'+1'
|
||||||
|
>>>
|
||||||
|
"""
|
||||||
|
if f1 == f2:
|
||||||
|
return '=='
|
||||||
|
if f1[0] == '0' and f2[0] == '0':
|
||||||
|
delta = int(f2[1]) - int(f1[1])
|
||||||
|
if delta == -1:
|
||||||
|
return '-1'
|
||||||
|
elif delta == 1:
|
||||||
|
return '+1'
|
||||||
|
return '!='
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
print(main(sys.stdin))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user