123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- # tf Text File manipulations
- # for micropython and other tiny environments
- # (c) Pat Beirne patb@pbeirne.com
- import re,os,sys,gc
- # ====legend====
- # e=end
- # f=input file
- # g=output file
- # i=iteration variable
- # m=line range
- # r=replace text
- # s=start/search/string
- # sp=search regex
- def transfer(src,dest,first=1,last=0xFFFFFFFF,numbers=False,grep_func=None):
- #src is a filename, dst is a handle
- i=0
- with open(src) as f:
- for lin in f:
- i=i+1
- if i<first or i>last:
- continue
- if grep_func and not grep_func(lin):
- continue
- if numbers:
- dest.write(str(i)+' ')
- dest.write(lin)
- def cp(src,dest):
- with open(dest,'w') as g:
- transfer(src,g)
- def cat(filename, first=1, last=1000000, numbers=False, title=True):
- if title:
- print("===={}====".format(filename))
- transfer(filename,sys.stdout,first,last,numbers=numbers)
- def grep(filename, pattern, numbers=False):
- m=re.compile(pattern)
- transfer(filename,sys.stdout,numbers=numbers,grep_func=(lambda x:m.search(x[:-1])))
- def sed(filename, sed_cmd, bak_ext=".bak"):
- # parse the sed_cmd
- # group 1,3 are the n-start, n-end group 4 is command: aidsxX
- a=re.search("^(\d*)([,-](\d+|\$))?\s*([sdaixX])(.*)",sed_cmd)
- if not a:
- raise ValueError("sed() failed; pattern must be a number-range followed by one of sdaixX; no changes applied")
- op,args=a.group(4),a.group(5)
- s,e=1,1000000
- if a.group(1):
- s=e=int(a.group(1))
- if a.group(3):
- e=1000000 if a.group(3)=='$' else int(a.group(3))
- if op in "aid" and e-s==1000000:
- raise ValueError("sed(a/i/d) should have a line number")
- #print("sed command parser of <{}> returned {} {} {}".format(op, cmd, a.group(1), a.group(3)))
- if op in "sxX":
- if len(args)<2 or args[0] in "\^$()[]":
- raise ValueError("invalid sed argument: "+op+args)
- dl=args[0]
- if op=='s':
- gs=re.search(dl+"([^"+dl+"]*)"+dl+"([^"+dl+"]*)"+dl+"(g)?",args)
- else:
- gs=re.search(dl+"([^"+dl+"]*)"+dl,args)
- if not gs:
- raise ValueError("invalid sed search pattern: "+op+args)
- ss=gs.group(1)
- if op=='s':
- r=gs.group(2)
- rm=0 if gs.group(3) else 1
- sp=re.compile(ss)
- args=args + '\n'
- try:
- os.rename(filename,filename+bak_ext)
- except:
- raise OSError("problem with filename",filename," backup failed; no changes made")
- i=h=0
- with open(filename+bak_ext) as f:
- with open(filename,'w') as g:
- for lin in f:
- i=i+1
- m=(s <= i <= e)
- if op=='s' and m:
- lin=lin[:-1]
- if sp.search(lin): h+=1
- lin=sp.sub(r,lin,rm)+'\n'
- if op=='d' and m:
- h+=1
- continue # delete line
- if op=='i' and m:
- #print("insert a line before {} <{}>".format(i,extra))
- g.write(args)
- h+=1
- if op in "aids":
- g.write(lin)
- elif m and (op=='x' if sp.search(lin) else op=='X'):
- g.write(lin)
- h+=1
- if op=='a' and m:
- #print("append a line after {} <{}>".format(i,extra))
- g.write(args)
- h+=1
- #f.write("--file modifed by sed()--\n")
- return (i, h)
- def _dir(d='.'):
- for f in os.listdir(d):
- s=os.stat(d+'/'+f)
- print("{}rwx all {:9d} {}".format('d' if (s[0] & 0x4000) else '-',s[6],f ))
- s=os.statvfs('/')
- print("disk size:{:8d} KB disk free: {} KB".format(s[0]*s[2]//1024,s[0]*s[3]//1024))
- '''-----cut here if you only need the functions-----'''
- def ext_cmd(a):
- return
- if 'tf_extend.py' in os.listdir():
- import tf_extend
- ext_cmd=tf_extend.cmd
- def _help():
- print("==Simple shell v1.2 for Text Files")
- print(" cp/copy <src-file> <dest-file>")
- print(" mv/move/rename <src-file> <dest-file> \trm/del <file>")
- print(" cd [<folder>]\t\tmkdir <folder>\t\trmdir <folder>")
- print(" dir/ls [<folder>]")
- print(" cat/list [-n] [-l <n>,<m>] <file>")
- print(" grep <pattern> <file>")
- print(" sed <pattern> <file>")
- print(" pattern is <line-range><op><extra> eg: 's/search/replace/' 'x!TODO:!' '43,49d' '8itext'")
- print(" patterns with spaces require '-quotes\tsed ops are one of s/d/i/a/x/X")
- print(" sed cannot cross line boundaries\t\tsed s/x/X-patterns: non-/ delimiters are ok")
- print("file names must NOT have embedded spaces\toptions must be early on the command line")
- ext_cmd('help')
- def parseQuotedArgs(st):
- #returns (pattern,file)
- st=st.strip()
- if st[0]=="'":
- p=re.search("'(.*?[^\\\\])'\s*(\S*)",st)
- if not p:
- print("error in quoted pattern:",st)
- return
- return p.group(1),p.group(2)
- else:
- return st.split(None,1)
- def main():
- print("Simple shell: cp/copy mv/move rm/del cat/list cd dir/ls mkdir rmdir grep sed help")
- while 1:
- numbers=False
- r=input(os.getcwd()+"$ ")
- rp=r.split()
- if not len(rp): continue
- op=rp[0]
- if op in ('dir','ls'):
- try:
- _dir(rp[1] if len(rp)>1 else '.')
- except:
- print("directory not found")
- elif op in ('cat','list'):
- n=(" -n " in r) #print line-nums
- s,e=1,1000000 #start/end
- g=re.search("\s(-l\s*(\d+)([-,](\d+|\$)?)?)\s+",r[3:])
- if g:
- s=e=int(g.group(2))
- if g.group(3):
- e=int(g.group(4)) if g.group(4) and g.group(4).isdigit() else 1000000
- try:
- cat(rp[-1],s,e,numbers=n)
- except:
- print("file not found",rp[-1])
- elif op in('grep','sed'):
- if len(rp)<3:
- print(op,"pattern filename")
- continue
- p=parseQuotedArgs(r[4:])
- if not p:
- continue
- try:
- if op=='grep':
- grep(p[1],p[0],numbers=True)
- else:
- r=sed(p[1],p[0])
- if r:
- print("Lines processed: {} Lines modifed: {}".format(*r))
- except (ValueError, OSError) as e:
- print(e)
- except RuntimeError:
- print("problem with the regex; try a different pattern")
- elif op=='help':
- _help()
- ext_cmd(rp)
- elif ext_cmd(rp):
- pass
- else:
- try:
- if op in ('cp','copy'):
- cp(rp[1],rp[2])
- elif op=='cd':
- os.chdir(rp[1] if len(rp)>1 else '/')
- elif op=='mkdir':
- os.mkdir(rp[1])
- elif op=='rmdir':
- os.rmdir(rp[1])
- elif op in('mv','move','rename'):
- os.rename(rp[1],rp[2])
- elif op in('rm','del'):
- os.remove(rp[1])
- else:
- print("command not implemented:",op)
- except IndexError:
- print("not enough argments; check syntax with 'help'")
- except OSError:
- print("file/folder not found or cannot be written")
- gc.collect()
-
- if __name__=="tf":
- print("tf module loaded; members cp(), cat(), _dir(), grep() and sed()")
- main()
|