pythonfor-looppexpect

Nested for-loop gets executed only once


I'm writing a simple Python:Pexpect script that is a replacement of an old TCL:Expect working script I used to push configuration changes or command to our network switches.

If I write:

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 contained in the "commands" variable get executed only once, in the first host of the list "hostls"

Instead if I write:

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+"***")

commands get correctly executed for every host of the "hostls" variable. What is that I do not understand?

Just for completeness, in the Tcl:Expect brother of this script, I've a logic similar to the one of the first example and it works correctly, so probably I'm missing something in my knowledge of Python. Has it something to do on how the block "try except" is managed? I searched info about it, but I didn't find anything meaningful about this specific argument.


Solution

  • The open() function returns a file object, which can be used to iterate over the lines in the file. You can only iterate over them once, though. In the first iteration of the outer loop you iterate over them, exhausting the iterator. From the second iteration on, no elements remain.

    Solution: either re-open the file on every iteration of the outer loop (which you did in your second code sample), or better store the commands in a list, which can be iterated over again and again:

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

    (Using 'with' ensures that the file is closed properly even in the case of exceptions)

    Edit: f.readlines() is clearer than list(f).