嵌套for循环只执行一次

0 投票
2 回答
63 浏览
提问于 2025-04-12 10:28

我正在写一个简单的Python:Pexpect脚本,目的是替代我以前用的一个老旧的TCL:Expect脚本,这个脚本用来向我们的网络交换机推送配置更改或命令。

如果我写:

h_ls = open(hostls,"r")
c_ls = open(commands,"r")
for host in h_ls:
  host = host.strip()
  try:
    s = pxssh.pxssh(timeout=5,logfile = sys.stdout,options={
                "StrictHostKeyChecking": "no",
                "UserKnownHostsFile": "/dev/null",
                "AddKeysToAgent": "yes"},encoding='utf-8')
    s.login (host, user, password,\
            auto_prompt_reset=False,\
            original_prompt=r'.*#')
    print (">>>>> Working on "+host+" @ "+str(now)+" <<<<<\n")
    s.prompt()
    for cmd in c_ls:
     s.sendline(cmd+"\n")
     s.prompt()
     print(s.before)
    s.logout()
  except pxssh.ExceptionPxssh as e:
    print("***pxssh failed on login***")
    #traceback.print_exc()
    print("***"+str(e)+" "+host+"***")

那么“commands”变量里的命令只会在“hostls”列表的第一个主机上执行一次。

但如果我写:

h_ls = open(hostls,"r")
for host in h_ls:
  host = host.strip()
  try:
    s = pxssh.pxssh(timeout=5,logfile = sys.stdout,options={
                "StrictHostKeyChecking": "no",
                "UserKnownHostsFile": "/dev/null",
                "AddKeysToAgent": "yes"},encoding='utf-8')
    s.login (host, user, password,\
            auto_prompt_reset=False,\
            original_prompt=r'.*#')
    print (">>>>> Working on "+host+" @ "+str(now)+" <<<<<\n")
    s.prompt()
    c_ls = open(commands,"r")
    for cmd in c_ls:
     s.sendline(cmd+"\n")
     s.prompt()
     print(s.before)
    s.logout()
  except pxssh.ExceptionPxssh as e:
    print("***pxssh failed on login***")
    #traceback.print_exc()
    print("***"+str(e)+" "+host+"***")

那么这些命令就会在“hostls”变量里的每个主机上正确执行。

我到底哪里没理解呢?

为了完整起见,在这个脚本的Tcl:Expect版本中,我的逻辑和第一个例子类似,而且它运行得很好,所以我可能在Python的知识上有些欠缺。这和“try except”块的处理方式有关吗?我查了一些资料,但没有找到关于这个特定问题的有用信息。

2 个回答

1

这不是关于错误处理的问题。注意一下c_ls的作用范围。在第一个例子中,你是在循环外打开文件的,这样对象只创建了一次,当你开始遍历它时,第一次遍历就把内容用完了,之后就没有命令可以执行给剩下的主机了。而在第二段代码中,你是在循环里面打开文件,这意味着每次处理一个主机时,都会创建一个新的c_ls对象,这样它就包含了所有可以遍历的命令。

1

open()函数会返回一个文件对象,这个对象可以用来逐行读取文件的内容。不过,你只能读取一次。在第一次循环中,你会读取所有的行,这样迭代器就用完了。从第二次循环开始,就没有内容可以读取了。

解决办法是:要么在每次外部循环时重新打开文件(这在你的第二段代码中已经做到了),要么更好的是把命令存储在一个列表里,这样可以多次读取:

with open(commands, "r") as f:
    c_ls = f.readlines()

(使用'with'可以确保即使出现异常,文件也能被正确关闭)

补充:f.readlines()比list(f)更清晰。

撰写回答