tf.py 6.8 KB

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