简体   繁体   中英

concision in a for loop without list comprehension

Please don't laugh. I'm trying to write a simple script that will replace the hostname and IP of a base VM. I have a working version of this, but I'm trying to make it more readable and concise. I'm getting a syntax error when trying the code below. I was trying to make these list comprehensions, but since they are file types, that won't work. Thanks in advance.

try:
   old_network = open("/etc/sysconfig/network", "r+")
   new_network = open("/tmp/network", "w+")
   replacement = "HOSTNAME=" + str(sys.argv[1]) + "\n"

   shutil.copyfile('/etc/sysconfig/network', '/etc/sysconfig/network.setup_bak')
   for line in old_network: new_network.write(line) if not re.match(("HOSTNAME"), line)
   for line in old_network: new_network.write(replacement) if re.match(("HOSTNAME"), line)
   os.rename("/tmp/network","/etc/sysconfig/network")
   print 'Hostname set to', str(sys.argv[1])
except IOError, e:
    print "Error %s" % e
    pass

You are using some odd syntax here:

for line in old_network: new_network.write(line) if not re.match(("HOSTNAME"), line)
for line in old_network: new_network.write(replacement) if re.match(("HOSTNAME"), line)

You need to reverse the if statement there, and put these on separate lines; you have to combine the loops, which lets you simplify the if statements too:

for line in old_network:
    if not re.match("HOSTNAME", line):
        new_network.write(line) 
    else:
        new_network.write(replacement)

You cannot really loop over an input file twice (your second loop wouldn't do anything as the file has already been read in full).

Next, you want to use the open file objects context managers (using with ) to make sure they are closed properly, whatever happens. You can drop the + from the file modes, you are not using the files in mixed mode, and the backup copy is probably best done first before opening anything for reading and writing just yet.

There is no need to use a regular expression here; you are testing for the presence of a straightforward simple string, 'HOSTNAME' in line will do, or perhaps line.strip().startswith('HOSTNAME') to make sure the line starts with HOSTNAME .

Use the tempfile module to create a temporary file with a name that won't conflict:

from tempfile import NamedTemporaryFile

shutil.copyfile('/etc/sysconfig/network', '/etc/sysconfig/network.setup_bak')

replacement = "HOSTNAME={}\n".format(sys.argv[1])
new_network = NamedTemporaryFile(mode='w', delete=False)

with open("/etc/sysconfig/network", "r") as old_network, new_network:
    for line in old_network:
        if line.lstrip().startswith('HOSTNAME'):
            line = replacement
        new_network.write(line) 

os.rename(new_network.name, "/etc/sysconfig/network")
print 'Hostname set to {}'.format(sys.argv[1])

You can simplify this even further by using the fileinput module , which lets you replace a file contents by simply printing; it supports creating a backup file natively:

import fileinput
import sys

replacement = "HOSTNAME={}\n".format(sys.argv[1])

for line in fileinput('/etc/sysconfig/network', inplace=True, backup='.setup_bak'):
    if line.lstrip().startswith('HOSTNAME'):
        line = replacement
    sys.stdout.write(line)

print 'Hostname set to {}'.format(sys.argv[1])

That's 6 lines of code (not counting imports) versus your 12. We can squash this down to just 4 by using a conditional expression, but I am not sure if that makes things more readable:

for line in fileinput('/etc/sysconfig/network', inplace=True, backup='.setup_bak'):
    sys.stdout.write(replacement if line.lstrip().startswith('HOSTNAME') else line)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM