In general, there is a configuration file, config_sw1.txt, with lines in it. There is a list of ignore with words. The task is to read lines from a file, output only those lines that do not begin with '!' and in which there are no words from the ignore list.

Code:

ignore = ['duplex', 'alias', 'Current configuration'] currentlist = [] with open('config_sw1.txt', 'r') as f: for line in f: if line[0] != '!' and line[0] != '\n': currentlist.append(line) for line in currentlist: for item in ignore: if item in line: currentlist.remove(line) for line in currentlist: print(line) 

It seems like it even works, but for some reason some of the lines are not deleted. There are about 6 lines left, starting with 'alias'.

Contents of the file 'config_sw1.txt':

 Current configuration : 2033 bytes ! ! Last configuration change at 13:11:59 UTC Thu Feb 25 2016 ! version 15.0 service timestamps debug datetime msec service timestamps log datetime msec no service password-encryption ! hostname sw1 ! ! ! ! ! ! ! ! ! ! ! interface Ethernet0/0 duplex auto ! ! ! ! ! ! ! ! interface Ethernet0/1 switchport trunk encapsulation dot1q switchport trunk allowed vlan 100 switchport mode trunk duplex auto spanning-tree portfast edge trunk ! interface Ethernet0/2 duplex auto ! interface Ethernet0/3 switchport trunk encapsulation dot1q switchport trunk allowed vlan 100 duplex auto switchport mode trunk spanning-tree portfast edge trunk ! interface Ethernet1/0 duplex auto ! interface Ethernet1/1 duplex auto ! interface Ethernet1/2 duplex auto ! interface Ethernet1/3 duplex auto ! interface Vlan100 ip address 10.0.100.1 255.255.255.0 ! ! alias configure sh do sh alias exec ospf sh run | s ^router ospf alias exec bri show ip int bri | exc unass alias exec id show int desc alias exec top sh proc cpu sorted | excl 0.00%__0.00%__0.00% alias exec c conf t alias exec diff sh archive config differences nvram:startup-config system:running-config alias exec shcr sh run | s ^crypto alias exec desc sh int desc | ex down alias exec bgp sh run | s ^router bgp alias exec xc sh xconnect all alias exec vc sh mpls l2tr vc ! line con 0 exec-timeout 0 0 privilege level 15 logging synchronous line aux 0 line vty 0 4 login transport input all ! end 

Python script output:

 version 15.0 service timestamps debug datetime msec service timestamps log datetime msec no service password-encryption hostname sw1 interface Ethernet0/0 interface Ethernet0/1 switchport trunk encapsulation dot1q switchport trunk allowed vlan 100 switchport mode trunk spanning-tree portfast edge trunk interface Ethernet0/2 interface Ethernet0/3 switchport trunk encapsulation dot1q switchport trunk allowed vlan 100 switchport mode trunk spanning-tree portfast edge trunk interface Ethernet1/0 interface Ethernet1/1 interface Ethernet1/2 interface Ethernet1/3 interface Vlan100 ip address 10.0.100.1 255.255.255.0 alias exec ospf sh run | s ^router ospf alias exec id show int desc alias exec c conf t alias exec shcr sh run | s ^crypto alias exec bgp sh run | s ^router bgp alias exec vc sh mpls l2tr vc line con 0 exec-timeout 0 0 privilege level 15 logging synchronous line aux 0 line vty 0 4 login transport input all end 

    4 answers 4

    The error of your code was that you are iterating through the list from which you are deleting elements.

     for line in currentlist: for item in ignore: if item in line: currentlist.remove(line) 

    Because of this, every second line in the sequence of lines with alias was skipped.

    To avoid this error, you can iterate over a copy of the list or generate a new list instead of changing the original one:

     # итерируемся по копии for line in list(currentlist): for item in ignore: if item in line: currentlist.remove(line) # генерируем новый result = [line for line in currentlist: for item in ignore: if item not in line] 

    For example, if there is a code

     lst = [1, 2, 3, 4] for value in lst: lst.remove(value) 

    then as a result we will get the following list

     print(lst) # [2, 4] 

    The fact is that the list iterator stores the item number in the list and the code is similar to the following:

     lst = [1, 2, 3, 4] i = 0 while i < len(lst): current = lst[i] lst.remove(current) i += 1 

    And if the deletion is successful, then the current index was not necessary to increase. However, it increases on each iteration of the loop, regardless of the change in the list, and because of this, you can see the effect when each value is skipped after the deleted item.

    • Thanks for the detailed answer! Now just figured out - kolan
     def line_filter(line: str, ignore=('duplex', 'alias', 'Current configuration')) -> bool: line = line.strip() return line and not line.startswith('!') and not any(map(line.__contains__, ignore)) currentlist = list(filter(line_filter, open('config_sw1.txt'))) # или так def line_filter(lines: iter, ignore=('duplex', 'alias', 'Current configuration')) -> iter: for line in lines: line = line.strip() if line and not line.startswith('!') and not any(map(line.__contains__, ignore)): yield line currentlist = list(line_filter(open('config_sw1.txt'))) 
    • Code Algorithm: using filter file object is iterated, returning lines from it that are filtered by line_filter , and the result is given to the list. The hardest part of line_filter : checking that line not empty, checking that its first character ! , using map each element from ignore is called in line.__contains__ and the results are returned as a collection (containing True or False), using the any function, we check that at least one element of that collection is True (which means that the word from ignore was found) but we need "normal" lines, so we deny the result any - gil9red
    • The only thing that confuses me in the code is in the line_filter function the ignore parameter, if I want to change or add, I have to wrap the function in the filter in lambda: my_ignore = ('duplex', 'alias', 'Current configuration', 'service') currentlist = list(filter(lambda x: line_filter(x, my_ignore), open('config_sw1.txt'))) or ignore global - gil9red
    • @ gil9red I think in the line_filter part, this is not how it works. any gets a generator, map gives bool results one by one, until the first one is true. Therefore, to say that, the results are returned in the form of a collection and then already any checks are incorrect. Adding to the list and checking the line, occurs line by line, without keeping all the data from the file in memory - vadim vaduxa
    • Thanks for the explanation :) - gil9red

    Here is a working example:

     def Example(path): ignore = ['duplex', 'alias', 'Current configuration'] currentlist = [] with open(path, 'r') as file: for i in file.readlines(): Flag = True if not str(i).startswith('!'): for j in ignore: if j in i: Flag = False if Flag: currentlist.append(i.strip()) for word in currentlist: print(word) 

    Using:

     Example(r'путь\к\файлу') 
    • I do not understand people who minus the correct answer. - Pavel Durmanov
    • What is wrong with it is that you lead to str (i), something that is already a str, while further referring to i.strip is no longer resulting. File name with '{}'. Format, use of the redundant variable Flag - vadim vaduxa
    • @vadimvaduxa As for the str & format you are right, too smart. Regarding Flag I use to check each word for line membership. Your option is clearly better, but for a beginner it is less clear. - Pavel Durmanov
    • 4 of your lines with Flag, can be replaced by one more understandable if not any (j in i for j in ignore): - vadim vaduxa

    Slightly corrected the author's algorithm:

     ignore = ['duplex', 'alias', 'Current configuration'] current_list = list() with open('config_sw1.txt', 'r') as f: for line in f: line = line.rstrip() # Если пустая строка if not line: continue # Если начинается на '!' if line[0] == '!': continue # Если в строке есть строки из списка игнора found_ignore = False for item in ignore: if item in line: found_ignore = True break if found_ignore: continue current_list.append(line) for line in current_list: print(line) 

    Console:

     version 15.0 service timestamps debug datetime msec service timestamps log datetime msec no service password-encryption hostname sw1 interface Ethernet0/0 interface Ethernet0/1 switchport trunk encapsulation dot1q switchport trunk allowed vlan 100 switchport mode trunk spanning-tree portfast edge trunk interface Ethernet0/2 interface Ethernet0/3 switchport trunk encapsulation dot1q switchport trunk allowed vlan 100 switchport mode trunk spanning-tree portfast edge trunk interface Ethernet1/0 interface Ethernet1/1 interface Ethernet1/2 interface Ethernet1/3 interface Vlan100 ip address 10.0.100.1 255.255.255.0 line con 0 exec-timeout 0 0 privilege level 15 logging synchronous line aux 0 line vty 0 4 login transport input all end 
    • Thank you very much! - kolan
    • @kolan, then accept the answer as correct (check mark to the left of the answer) and check that the answer is useful (up arrow);) - gil9red
    • @kolan, this is not the most unfortunate code, found_ignore, continue, break is all this superfluous, look at my answer - vadim vaduxa
    • @vadimvaduxa, I agree, your code is shorter, but the functional style confuses it, it’s better to write a few lines more, but even beginners understand it, than with a bunch of functions and lambdas nested in each other, which even the author doesn’t immediately understand ( , in a few months) :) - gil9red
    • @ gil9red I mean that any should have been used, not found_ignore - vadim vaduxa