tf.py 6.8 KB

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