¿Cuál es la forma más rápida de encontrar la posición de byte de una línea específica en un archivo desde la línea de comando?
p.ej
$ linepos myfile.txt 13 5283 Estoy escribiendo un analizador para un CSV que tiene varios GB de tamaño y, en caso de que el analizador se detenga, me gustaría poder reanudar desde la última posición. El analizador está en Python, pero incluso iterar sobre file.readlines() lleva mucho tiempo, ya que hay millones de filas en el archivo. Me gustaría simplemente hacer file.seek(int(command.getoutput("linepos myfile.txt %i" % lastrow))) , pero no puedo encontrar un comando de shell para hacer esto de manera eficiente.
Editar: Perdón por la confusión, pero estoy buscando una solución que no sea de Python. Ya sé cómo hacer esto desde Python.
Del comentario de @chepner sobre mi otra respuesta:
position = 0 # or wherever you left off last time try: with open('myfile.txt') as file: file.seek(position) # zero in base case for line in file: position = file.tell() # current seek position in file # process the line except: print 'exception occurred at position {}'.format(position) raiseLa iteración sobre un objeto de archivo produce líneas con finales de línea completos intactos. Debería poder simplemente agregar la len a un objeto contador para obtener la posición. Deberá multiplicar según la codificación de caracteres (tamaño de bytes de carácter)
position = 0 # or wherever you left off last time try: with open('myfile.txt') as file: # don't you go correcting me on naming it file. we don't call file directly anyway! file.seek(position) # zero in base case for line in file: position += len(line) # process the line except: # yes, a naked exception. TWO faux pas in one answer?!? print 'exception occurred at position {}'.format(position) raise # re-raise to see traceback or what have youBueno, si tu patrón es simple, este sería simple.
$ echo -e '#!/bin/bash\necho abracadabra' >/tmp/script $ pattern=bash $ sed -rn "0,/$pattern/ {s/^(.*)$pattern.*$/\1/p ;t exit; p; :exit }" /tmp/script \ | wc -c 8Como puede ver, esto generará la posición del primer carácter en su patrón, asumiendo que el primer carácter en el archivo tiene el número 1.
NB 1: sed tiene la costumbre de agregar una nueva línea final a la última cadena que analiza, por lo tanto, cuando tomamos una parte de la línea que precede al pattern , el número de bytes en la salida debe ser 7 (cuéntelos → #!/bin/ ), pero lo que realmente cuenta wc -c parece
$ sed -rn "0,/$pattern/ {s/^(.*)$pattern.*$/\1/p ;t exit; p; :exit }" /tmp/script \ | hexdump -C 00000000 23 21 2f 62 69 6e 2f 0a |#!/bin/.| 00000008Esto podría ser una fuente potencial de problemas, si estuviera buscando EOF, por ejemplo. No puedo pensar en un caso más apropiado, solo quiero señalarlo.
NB 2: si el patrón contendrá caracteres especiales, sed fallará. Si pudiera proporcionar un ejemplo de lo que está buscando, podría escapar.
NB 3: Esto supone que el pattern es único. Si deja de leer el archivo en una segunda o tercera instancia de pattern , esto no funcionará.
Actualizar. He encontrado una forma más sencilla.
$ grep -bo bash <<< '#!/bin/bash' 7:bashPara GNU grep hay dos opciones:
-b, --byte-offset Print the 0-based byte offset within the input file before each line of output. If -o (--only-matching) is specified, print the offset of the matching part itself. Sugeriría usar grep, porque si especifica la tecla -F , tratará el patrón como una cadena simple.
$ grep -F '!@##$@#%%^%&*%^&*(^)((**%%^@#' <<<'!@##$@#%%^%&*%^&*(^)((**%%^@#' !@##$@#%%^%&*%^&*(^)((**%%^@#