6

每当我的 IP 地址更改时,我都需要更新一个文本文件,然后再从 shell 运行一些命令。

  1. 创建变量 LASTKNOWN = "212.171.135.53" 这是我们在编写此脚本时拥有的 IP 地址。

  2. 获取当前 IP 地址。它将每天更改。

  3. 为新 IP 创建变量 CURRENT。

  4. 比较(作为字符串)CURRENT 和 LASTKNOWN

  5. 如果它们相同,则退出()

  6. 如果它们不同,

    A. 将包含 LASTKNOWN IP 地址的旧配置文件 (/etc/ipf.conf) “复制”到 /tmp B. 在 /tmp/ipf.conf 文件中将 LASTKNOWN 替换为 CURRENT。
    C. 使用子进程“mv /tmp/ipf.conf /etc/ipf.conf”
    D. 使用子进程执行,“ipf -Fa -f /etc/ipf.conf”
    E. 使用子进程执行,“ipnat -CF -f /etc/ipnat.conf"

  7. 出口()

我知道如何执行步骤 1 到 6。我陷入了“文件编辑”部分,A -> C。我不知道要使用哪个模块,或者我是否应该就地编辑文件。有很多方法可以做到这一点,我无法决定最好的方法。我想我想要最保守的一个。

我知道如何使用子流程,因此您无需对此发表评论。

我不想替换整行;只是一个特定的虚线四边形。

谢谢!

4

6 回答 6

27

另一种简单地编辑文件的方法是使用该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)
于 2010-03-02T14:43:26.090 回答
6

在 /etc/ipf.conf 中将 LASTKNOWN 替换为 CURRENT

一次性全部更换

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()或类似
注意:代码未经测试。

于 2009-10-18T06:47:31.167 回答
4

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)

与文件输入的主要区别:

  • 不是劫持 sys.stdout,而是返回一个新的文件句柄进行写入。
  • 文件句柄支持所有标准 I/O 方法,而不仅仅是 readline()。

例如 - fileinput 只能逐行编辑,in_pace 允许将整个文件读取到内存(如果它不大)并修改它。

于 2018-04-17T08:53:18.900 回答
2

您正在尝试“原子地”更新文件的内容,并且在该主题上发生了许多令人愉快的火焰战争。但一般模式是:

1)将新文件写入临时文件,并确保刷新并关闭。

2) 使用您的操作系统的工具将临时文件自动重命名为旧文件。

现在,您根本无法在 Windows 中以原子方式重命名文件,但听起来您无论如何都在类似 unix 的系统上。您使用os.rename()原子地重命名。

于 2009-10-17T17:47:23.743 回答
1

可能最简单的方法是使用 f=open(filename, mode) 打开文件。然后,使用 f.readlines() 读取所有行(这将返回代表程序行的字符串列表)。

然后,您可以搜索这些字符串以找到地址并将其替换为新地址(使用标准字符串替换、正则表达式或任何您想要的)。

最后,您可以使用 f.writelines(lines) 将行写回文件,它可以方便地取回行列表。

注意:这不是一种有效的方法,它只是最简单的。请

示例代码:

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.
于 2009-10-17T17:45:26.213 回答
0

在检查了您放在 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 时使用较少的子进程调用。

于 2009-10-17T18:37:14.500 回答