Editar archivo de texto usando Python

Necesito actualizar un archivo de texto cada vez que cambie mi dirección IP y luego ejecutar algunos comandos desde el shell.

  1. Cree la variable LASTKNOWN = “212.171.135.53” Esta es la dirección IP que tenemos al escribir este script.

  2. Obtenga la dirección IP actual. Cambiará a diario.

  3. Crea la variable CURRENT para la nueva IP.

  4. Comparar (como cadenas) ACTUAL a LASTKNOWN

  5. Si son iguales, salir ().

  6. Si difieren,

    A. “Copie” el archivo de configuración antiguo (/etc/ipf.conf) que contiene la dirección IP LASTKNOWN en / tmp B. Reemplace LASTKNOWN con CURRENT en el archivo /tmp/ipf.conf.
    C. Utilizando el subproceso “mv /tmp/ipf.conf /etc/ipf.conf”
    D. Usando subprocess ejecute, “ipf -Fa -f /etc/ipf.conf”
    E. Utilizando la ejecución de subproceso, “ipnat -CF -f /etc/ipnat.conf”

  7. salida()

Sé cómo hacer los pasos del 1 al 6. Caigo en la parte de “edición de archivos”, A -> C. No puedo saber qué módulo usar o si debo editar el archivo en su lugar. Hay tantas formas de hacer esto que no puedo decidir cuál es el mejor enfoque. Supongo que quiero el más conservador.

Sé cómo usar el subproceso, por lo que no necesita comentar sobre eso.

No quiero reemplazar líneas enteras; sólo un quad punteado específico.

¡Gracias!

El módulo de entrada de archivo tiene una API muy fea. Encuentro un módulo hermoso para esta tarea – in_place , ejemplo para Python 3:

 import in_place with in_place.InPlace('data.txt') as file: for line in file: line = line.replace('test', 'testZ') file.write(line) 

principal diferencia de fileinput:

  • En lugar de secuestrar sys.stdout, se devuelve un nuevo identificador de archivo para la escritura.
  • El identificador de archivo admite todos los métodos de E / S estándar, no solo readline ().

por ejemplo, la entrada de archivos solo puede editarse línea por línea, en espacio permite leer todo el archivo en la memoria (si no es grande) y modificarlo.

Otra forma de editar simplemente los archivos en su lugar es usar el módulo de entrada de fileinput :

 import fileinput, sys for line in fileinput.input(["test.txt"], inplace=True): line = line.replace("car", "truck") # sys.stdout is redirected to the file sys.stdout.write(line) 

Reemplace LASTKNOWN por CURRENT en /etc/ipf.conf

Reemplazar todos a la vez

 filename = "/etc/ipf.conf" text = open(filename).read() open(filename, "w").write(text.replace(LASTKNOWN, CURRENT)) 

Reemplazar línea por línea

 from __future__ import with_statement from contextlib import nested in_filename, outfilename = "/etc/ipf.conf", "/tmp/ipf.conf" with nested(open(in_filename), open(outfilename, "w")) as in_, out: for line in in_: out.write(line.replace(LASTKNOWN, CURRENT)) os.rename(outfilename, in_filename) 

Nota: “/tmp/ipf.conf” debe reemplazarse por tempfile.NamedTemporaryFile() o similar
Nota: el código no está probado.

Está intentando actualizar “atómicamente” el contenido de un archivo, y ha habido muchas guerras de llamas encantadoras sobre el tema. Pero el patrón general es:

1) Escriba el nuevo archivo en un archivo temporal, y asegúrese de vaciar y cerrar.

2) Utilice las facilidades de su sistema operativo para cambiar atómicamente el archivo temporal al archivo antiguo.

Ahora, simplemente no puede cambiar el nombre de un archivo en Windows, pero de todos modos parece que está en un sistema similar a Unix. Cambia el nombre atómicamente utilizando os.rename () .

Probablemente, la forma más sencilla sería abrir un archivo usando f = abrir (nombre de archivo, modo). Luego, lea todas las líneas usando f.readlines () (esto devolverá una lista de cadenas que representan las líneas del progtwig).

Luego, puede buscar en estas cadenas para encontrar la dirección y reemplazarla por una nueva (mediante el reemplazo de cadenas estándar, expresiones regulares o lo que desee).

Al final, puede volver a escribir las líneas en el archivo utilizando f.writelines (líneas), lo que convenientemente recupera una lista de líneas.

NOTA: Esta no es una forma eficiente de hacerlo, es la más fácil. Por favor

Código de ejemplo:

 f = open(filename, "r") lines = f.readlines() # Assume that change_ip is a function that takes a string and returns a new one with the ip changed): example below ret_lines = [change_ip(lines) for line in lines] new_file = open(new_filename, "w") new_file.writelines(lines) def change_ip(str): ''' Gets a string, returns a new string where the ip is changed ''' # Add implementation, something like: return str.replace(old_ip, new_ip) or something similair. 

Después de revisar los comentarios y el código que pones en pastebin, aquí hay una solución que funciona. Para empezar, el archivo /tmp/iiiipf.conf contiene:

 Simply a test file 175.48.204.168 And two times 175.48.204.168 on this line 175.48.204.168 Done. 

Después de ejecutar el código, el archivo /tmp/iiiipf.conf contiene:

 Simply a test file 10.73.144.112 And two times 10.73.144.112 on this line 10.73.144.112 Done. 

Y aquí está el código de trabajo probado con mis cosas fusionadas en su código pastebin:

 import socket import fileinput import subprocess import string import re CURRENT = socket.getaddrinfo(socket.gethostname(), None)[0][4][0] LASTKNOWN = '175.48.204.168' if CURRENT == LASTKNOWN: print 'Nevermind.' subprocess.sys.exit() else: cf = open("/tmp/iiiipf.conf", "r") lns = cf.readlines() # close it so that we can open for writing later cf.close() # assumes LASTKNOWN and CURRENT are strings with dotted notation IP addresses lns = "".join(lns) lns = re.sub(LASTKNOWN, CURRENT, lns) # This replaces all occurences of LASTKNOWN with CURRENT cf = open("/tmp/iiiipf.conf", "w") cf.write(lns) cf.close() 

Este bit de código hará lo que necesita, incluso si la dirección IP se usa varias veces dentro del archivo de configuración. También lo cambiará en líneas de comentarios.

Este método no requiere copiar a / tmp y utiliza una llamada de subproceso menos al reiniciar el firewall y NAT.