tf.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. # tf Text File manipulations
  2. # for micropython and other tiny environments
  3. # (c) Pat Beirne patb@pbeirne.com
  4. import re,os,sys,gc
  5. # ====legend====
  6. # e=end
  7. # f=input file
  8. # g=output file
  9. # i=iteration variable
  10. # m=line range
  11. # r=replace text
  12. # s=start/search/string
  13. # sp=search regex
  14. def transfer(src,dest,first=1,last=0xFFFFFFFF,numbers=False,grep_func=None):
  15. #src is a filename, dst is a handle
  16. i=0
  17. with open(src) as f:
  18. for lin in f:
  19. i=i+1
  20. if i<first or i>last:
  21. continue
  22. if grep_func and not grep_func(lin):
  23. continue
  24. if numbers:
  25. dest.write(str(i)+' ')
  26. dest.write(lin)
  27. def cp(src,dest):
  28. with open(dest,'w') as g:
  29. transfer(src,g)
  30. def cat(filename, first=1, last=1000000, numbers=False, title=True):
  31. if title:
  32. print("===={}====".format(filename))
  33. transfer(filename,sys.stdout,first,last,numbers=numbers)
  34. def grep(filename, pattern, numbers=False):
  35. m=re.compile(pattern)
  36. transfer(filename,sys.stdout,numbers=numbers,grep_func=(lambda x:m.search(x[:-1])))
  37. def sed(filename, sed_cmd, bak_ext=".bak"):
  38. # parse the sed_cmd
  39. # group 1,3 are the n-start, n-end group 4 is command: aidsxX
  40. a=re.search("^(\d*)([,-](\d+|\$))?\s*([sdaixX])(.*)",sed_cmd)
  41. if not a:
  42. raise ValueError("sed() failed; pattern must be a number-range followed by one of sdaixX; no changes applied")
  43. op,args=a.group(4),a.group(5)
  44. s,e=1,1000000
  45. if a.group(1):
  46. s=e=int(a.group(1))
  47. if a.group(3):
  48. e=1000000 if a.group(3)=='$' else int(a.group(3))
  49. if op in "aid" and e-s==1000000:
  50. raise ValueError("sed(a/i/d) should have a line number")
  51. #print("sed command parser of <{}> returned {} {} {}".format(op,cmd,a.group(1),a.group(3)))
  52. if op in "sxX":
  53. if len(args)<2 or args[0] in "\^$()[]":
  54. raise ValueError("invalid sed argument: "+op+args)
  55. dl=args[0]
  56. if op=='s':
  57. gs=re.search(dl+"([^"+dl+"]*)"+dl+"([^"+dl+"]*)"+dl,args)
  58. else:
  59. gs=re.search(dl+"([^"+dl+"]*)"+dl,args)
  60. if not gs:
  61. raise ValueError("invalid sed search pattern: "+op+args)
  62. ss=gs.group(1)
  63. if op=='s':
  64. r=gs.group(2)
  65. sp=re.compile(ss)
  66. args=args + '\n'
  67. try:
  68. os.rename(filename,filename+bak_ext)
  69. except:
  70. raise OSError("problem with filename",filename," backup failed; no changes made")
  71. i=h=0
  72. with open(filename+bak_ext) as f:
  73. with open(filename,'w') as g:
  74. for lin in f:
  75. i=i+1
  76. m=(i>=s and i<=e)
  77. if op=='s' and m:
  78. lin=lin[:-1]
  79. if sp.search(lin): h+=1
  80. lin=sp.sub(r,lin)+'\n'
  81. if op=='d' and m:
  82. h+=1
  83. continue # delete line
  84. if op=='i' and m:
  85. #print("insert a line before {} <{}>".format(i,extra))
  86. g.write(args)
  87. h+=1
  88. if op in "aids":
  89. g.write(lin)
  90. elif m and (op=='x' if sp.search(lin) else op=='X'):
  91. g.write(lin)
  92. h+=1
  93. if op=='a' and m:
  94. #print("append a line after {} <{}>".format(i,extra))
  95. g.write(args)
  96. h+=1
  97. #f.write("--file modifed by sed()--\n")
  98. return (i, h)
  99. def _dir(d='.'):
  100. for f in os.listdir(d):
  101. s=os.stat(d+'/'+f)
  102. print("{}rwx all {:9d} {}".format('d' if (s[0] & 0x4000) else '-',s[6],f))
  103. s=os.statvfs('/')
  104. print("disk size:{:8d} KB disk free: {} KB".format(s[0]*s[2]//1024,s[0]*s[3]//1024))
  105. '''-----cut here if you only need the functions-----'''
  106. def ext_cmd(a):
  107. return
  108. if 'tf_extend.py' in os.listdir():
  109. import tf_extend
  110. ext_cmd=tf_extend.cmd
  111. def _help():
  112. print("==Simple shell v1.2 for Text Files")
  113. print(" cp/copy <src-file> <dest-file>")
  114. print(" mv/move <src-file> <dest-file> \t\trm/del <file>")
  115. print(" cd [<folder>]\t\tmkdir <folder>\t\trmdir <folder>")
  116. print(" dir/ls [<folder>]")
  117. print(" cat/list [-n] [-l <n>,<m>] <file>")
  118. print(" grep <pattern> <file>")
  119. print(" sed <pattern> <file>")
  120. print(" pattern is <line-range><op><extra> e.g'a/search/replace/', 'x!TODO:!', '43,49d', '8itext'")
  121. print(" patterns with spaces require '-quotes\tsed ops are one of s/d/i/a/x/X")
  122. print(" sed cannot cross line boundaries\t\tsed s/x/X-patterns: non-/ delimiters are ok")
  123. print("file names must NOT have embedded spaces\toptions must be early on the command line")
  124. ext_cmd('help')
  125. def parseQuotedArgs(st):
  126. #returns (pattern,file)
  127. st=st.strip()
  128. if st[0]=="'":
  129. p=re.search("'(.*?[^\\\\])'\s*(\S*)",st)
  130. if not p:
  131. print("error in quoted pattern:",st)
  132. return
  133. return p.group(1),p.group(2)
  134. else:
  135. return st.split(None,1)
  136. def main():
  137. print("Simple shell: cp/copy mv/move rm/del cat/list cd dir/ls mkdir rmdir grep sed help")
  138. while 1:
  139. numbers=False
  140. r=input(os.getcwd()+"$ ")
  141. rp=r.split()
  142. if not len(rp): continue
  143. op=rp[0]
  144. if op in ('dir','ls'):
  145. try:
  146. _dir(rp[1] if len(rp)>1 else '.')
  147. except:
  148. print("directory not found")
  149. elif op in ('cat','list'):
  150. n=(" -n " in r) #print line-nums
  151. s,e=1,1000000 #start/end
  152. g=re.search("\s(-l\s*(\d+)([-,](\d+|\$)?)?)\s+",r[3:])
  153. if g:
  154. s=e=int(g.group(2))
  155. if g.group(3):
  156. e=int(g.group(4)) if g.group(4) and g.group(4).isdigit() else 1000000
  157. try:
  158. cat(rp[-1],s,e,numbers=n)
  159. except:
  160. print("file not found",rp[-1])
  161. elif op in('grep','sed'):
  162. if len(rp)<3:
  163. print(op,"pattern filename")
  164. continue
  165. p=parseQuotedArgs(r[4:])
  166. if not p:
  167. continue
  168. try:
  169. if op=='grep':
  170. grep(p[1],p[0],numbers=True)
  171. else:
  172. r=sed(p[1],p[0])
  173. if r:
  174. print("Lines processed: {} Lines modifed: {}".format(*r))
  175. except (ValueError, OSError) as e:
  176. print(e)
  177. except RuntimeError:
  178. print("problem with the regex; try a different pattern")
  179. elif op=='help':
  180. _help()
  181. ext_cmd(rp)
  182. elif ext_cmd(rp):
  183. pass
  184. else:
  185. try:
  186. if op in ('cp','copy'):
  187. cp(rp[1],rp[2])
  188. elif op=='cd':
  189. os.chdir(rp[1] if len(rp)>1 else '/')
  190. elif op=='mkdir':
  191. os.mkdir(rp[1])
  192. elif op=='rmdir':
  193. os.rmdir(rp[1])
  194. elif op in('mv','move'):
  195. os.rename(rp[1],rp[2])
  196. elif op in('rm','del'):
  197. os.remove(rp[1])
  198. else:
  199. print("command not implemented:",op)
  200. except IndexError:
  201. print("not enough argments; check syntax with 'help'")
  202. except OSError:
  203. print("file/folder not found or cannot be written")
  204. gc.collect()
  205. if __name__=="tf":
  206. print("tf module loaded; members cp(), cat(), cd(), _dir(), grep() and sed()")
  207. main()
  208. # grep 12.*\) dmesg fails