редактирование текстового файла с помощью Python

Мне нужно обновить текстовый файл всякий раз, когда меняется мой IP-адрес, а затем запустить несколько команд из оболочки.

  1. создать переменную последнего известные = "212.171.135.53" Это ip-адрес, который мы имеем при написании этого сценария.

  2. получить текущий IP-адрес. Он будет меняться ежедневно.

  3. создать переменный ток для нового IP.

  4. сравнить (как строки) текущий Последнего известные

  5. Если они одинаковы, exit ()

  6. Если они отличаются,

    A. "скопируйте" старый файл конфигурации (/etc / ipf.conf), содержащий последний известный IP-адрес в /tmp B. замените LASTKNOWN на CURRENT в /tmp / ipf.файл conf.
    C. использование подпроцесса " mv/tmp / ipf.conf/etc / ipf.conf"
    Д. Используя подпроцесса выполнение, "МГЛ -ФА -Ф /и т. д./МГЛ.conf"
    E. использование подпроцесса execute, " ipnat-CF-f /etc/ipnat.conf"

  7. exit ()

Я знаю, как сделать шаги с 1 по 6. Я падаю на часть "редактирование файла", A -> C. Я не могу сказать, какой модуль использовать или должен ли я редактировать файл на месте. Есть так много способов сделать это, я не могу выбрать лучший подход. Думаю, я хочу самый консервативный.

Я знаю, как использовать подпроцесс, поэтому вам не нужно комментировать.

Я не хочу заменять целые строки; просто конкретный пунктирный квадроцикл.

спасибо!

6 ответов


модуль fileinput имеет очень уродливый API, я нахожу красивый модуль для этой задачи -in_place, пример для 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)

основное отличие от fileinput:

  • вместо угона sys.stdout, для записи возвращается новая файловая ручка.
  • filehandle поддерживает все стандартные методы ввода-вывода, а не только readline().

например-fileinput может только по строкам редактировать, in_pace позволяет читать весь файл к памяти (если она не большая) и модифицировать ее.


другой способ просто редактировать файлы на месте-использовать 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)

заменить LASTKNOWN на CURRENT в /etc / ipf.conf

заменить все сразу

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

заменить строку за строкой

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)

Примечание: "/ tmp / ipf.conf " следует заменить на tempfile.NamedTemporaryFile() или подобное
Примечание: код не проверял.


вы пытаетесь "атомарно" обновить содержимое файла, и на эту тему было много восхитительных войн пламени. Но общая картина такова:--1-->

1) Запишите новый файл во временный файл и убедитесь, что вы сбросили и закрыли.

2) Используйте средства вашей операционной системы для атомарного переименования временного файла в старый файл.

теперь вы просто не можете атомарно переименовать файл в Windows, но похоже, что вы все равно находитесь в unix-подобной системе. Вы атомарно переименовать с помощью os.переименовать().


вероятно, самым простым способом было бы открыть файл с помощью f=open (filename, mode). Затем прочитайте все строки, используя f.readlines () (это вернет список строк, представляющих строки программы).

затем вы можете найти эти строки, чтобы найти адрес и заменить его новым (используя стандартную замену строк, регулярные выражения или все, что вы хотите).

в конце вы можете записать строки обратно в файл, используя f.writelines (строки), которые удобно возвращает список строк.

Примечание: это не эффективный способ сделать это, это просто самый простой. Пожалуйста!--2-->

пример кода:

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.

после проверки комментариев и бит кода, который вы положили на pastebin, вот рабочее решение. Начнем с файла / tmp / iiiipf.conf содержит:

Simply a test file 175.48.204.168

And two times 175.48.204.168 on this line 175.48.204.168

Done.

после запуска кода, файл / tmp / iiiipf.conf содержит:

Simply a test file 10.73.144.112

And two times 10.73.144.112 on this line 10.73.144.112

Done.

и вот проверенный рабочий код с моим материалом, объединенным в ваш код 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()

этот код будет делать то, что вам нужно, даже если IP-адрес используется несколько раз в конфигурации файл. Он также изменит его в строках комментариев.

этот метод не требует копирования в /tmp и использует один вызов подпроцесса при перезапуске брандмауэра и NAT.