3

I thought this would be easy, after 3 hours of searching and trial and error it appears not to be easy.

All I want to do is loop a search for a string until the string is found. I'm searching a log file for a string that appears when a condition arises e.g. when the string "function test 1" appears in the log I need to find it then execute another function.

Finding it is no problem, the problem is looping until it's found.

This finds perfectly:

for line in open(WebPath + SmokeTest): #these are variables I use to construct the path
    if 'readfalseloop2' in line:
            print True
            f = open(WebPath + SmokeTest,'a')
            f.write('<font color= "#347C2C">readfalseloop2</font><br />')
            f.close()
            break
    else:
        print False

I want to execute this until the word is found. Ideally I'd like to embed this in multiple functions, i don't want a separate def at this point.

I've not had success with any looping construct and YES I've looked at python docs, searched this site and ubuntu forum.

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
Surfdork
  • 91
  • 1
  • 8
  • 2
    The loop looks good to me. What exactly happens? If you want to embed this into multiple functions, I would put this into its own function. Or do you mean that you are somehow monitoring the log file and a soon as the file is changed, your code has to detect this change and test whether it contains a certain string? – Felix Kling Feb 01 '11 at 01:19
  • Yes, that's exactly what I'm trying to do. – Surfdork Feb 03 '11 at 00:47
  • I've yet to find a working example online for how to do this without using a 3rd party library which I'm prevented from doing in Sikuli – Surfdork Feb 03 '11 at 01:35

3 Answers3

4

Nice code Andrew. I wasn't informed of the existence of the for/else possibility.

But to temper the activity of machine's processes, it is possible to avoid repeated opening and closing of the file, and it is preferable to cool down the frequency of the verification IMO

from time import sleep

with open(WebPath + SmokeTest,'a+') as f:
    while True:
        if 'readfalseloop2' in f.read():
            f.seek(0,1)
            f.write('\n<font color= "#347C2C">readfalseloop2</font><br />')
            print True
            break
        print '~',
        f.seek(0,0)
        sleep(2)

This code works, I tested it . But only if the changing is performed through another programm. When I tried to modify the file by inserting the

<font color= "#347C2C">readfalseloop2</font><br />

chain manually, Windows refused to close the file with the changing.

.

After the f.read() , the file's pointer f must be reactivated to make it possible to write the

<font color= "#347C2C">readfalseloop2</font><br />

chain at the end of the file's content.

I don't know in what consists this reactivation. I only know that if the f.seek(0,1) instruction isn't executed, the process can't switch from a 'read' mode to a 'write' mode.

f.seek(0,1) means "move of 0 characters from your current position"; It isn't useful to give another order since the pointer is already at the end of the file, and that it would anyway come back at the end of the file before to begin to write in case it was elsewhere: that's the 'a' mode characteristic. So, even if the pointer would be positionned again at the beginning of the file by a f.seek(0,0), the writing would be done at the end.

;

In case the test if 'readfalseloop2' in f.read() gives False, the pointer must be moved by f.seek(0,0) to the very beginning of the file for the new following reading of the entire file's content.

.

Warning: I don't know what could happen in case the file is written in utf-8, because in utf-8 the characters are not represented by the same length of bytes, it depends on the character. In my opinion, it should work correctly even with utf-8

.


EDIT

I found a clearer and shorter code:

from time import sleep

with open(WebPath + SmokeTest,'r+') as f:
    while not 'readfalseloop2' in f.read():
        print '~',
        f.seek(0,0)
        sleep(2)

    f.seek(0,1)
    f.write('\n<font color= "#347C2C">readfalseloop2</font><br />')
    print 'True'

Or

from time import sleep

with open(WebPath + SmokeTest,'r') as f, open(WebPath + SmokeTest,'a') as g:
    while not 'readfalseloop2' in f.read():
        print '~',
        f.seek(0,0)
        sleep(2)

    g.write('\n<font color= "#347C2C">readfalseloop2</font><br />')
    print 'True'

8 lines. Python is fantastic

eyquem
  • 26,771
  • 7
  • 38
  • 46
  • This is definitely better in the sense that the file is not opened and closed, but reading the entire file at once could be an issue if the file is large, still a +1. – Andrew Clark Feb 01 '11 at 17:02
  • @Andrew Yes, indeed. Having a big file to process is still causing the same annoying specific problem. If it is read in one time, it is heavy for the memory; if it is read one line at a time, it can be long. I have no universal solution. In the present case, user577229 wants to treat a log file, which may be not too big. – eyquem Feb 01 '11 at 17:14
  • Adding single quotes to the names causes the code to fail, jython does not know what to do with it. I've not had any success in making these snippets function – Surfdork Feb 03 '11 at 01:28
  • java.nio.channels.NonReadableChannelException: with open(WebPath + SmokeTest,'a+') as f: while True: if "test" in f.read(): f.write('\nreadfalseloop2
    ') f.seek(0,1) print True #break print '~', f.seek(0,0) sleep(2)
    – Surfdork Feb 03 '11 at 01:31
  • What I'm attempting to do is read a log file until a specific value apears. once that appears the automation i'm using will know they are in synch with the other machine and will resume script execution. Is this possible? The log file is really small as in 1 to 15 kmax. – Surfdork Feb 03 '11 at 01:33
  • @user577229 Excuse me, there was a mistake in my code at the end. There was with open('WebPath + SmokeTest','r') as f, open('teoting.txt','a') as g: But single quotes have not to be here, since WebPath + SmokeTest is a so-called "variable" "containing" the name of YOUR file, nor the name 'teoting.txt' had to be here. I forgot to correct the code I used to test. It must be the reason of the error you speak about. – eyquem Feb 04 '11 at 20:50
  • @user577229 I tested my codes and they work. Naturally, I used another code to modify the content of the file , but it is simple and I didn't post it. In your second comment above, I see if "test" in f.read() . If you really want to detect the presence of the chain's "test" value, it must be so. But if test is a variable, there must not be quotes. Moreover, why is there # before break in your comment ? – eyquem Feb 04 '11 at 20:59
  • I really like the "with" syntax but it isn't available on many Python installations that I need to use. – vy32 Feb 05 '11 at 20:14
  • @eyquem Thanks for taking the time to correct the code, a noob like me appreciates it. – Surfdork Feb 11 '11 at 17:04
1

The problem with your program is that are reading through the input file and writing to the output file. So if you find a single instance of the requested input your program will go into an infinite loop, as it will keep reading the last line, re-appending it to the output file, which is the input file, and then repeating.

I'd suggest a program like this:

import re

r = re.compile("readflaseloop2")
in_fn = WebPath + inputName
outf = open(in_fn + ".log","a")
count = 0
for line in open(in_fn):
    m = r.search(line)
    if m:
    if count==0:
            print True
        outf.write("<font color='#347C2C'>%s</font><br>\n" % m.group(0))
        outf.flush()
        count += 1
if count==0:
    print False
vy32
  • 28,461
  • 37
  • 122
  • 246
  • 1
    why do r.search(line) instead of r.search(content_of_file) ? – eyquem Feb 01 '11 at 04:36
  • Because I want to see every line that matches. With r.search(content_of_file) you need to then splice the output that you've processed from the output you haven't processed, which will significantly increase memory utilization, should you want to get the next match. But apparently that's not what the creator wanted. – vy32 Feb 05 '11 at 20:15
1

As vy32 noted, writing to the same file you're currently reading can be tricky (with the exact behaviour being OS dependent).

It also isn't entirely clear if the file you're reading is being updated live by another process. I'm going to assume that is the case, as simple line iteration would otherwise do the trick.

When reading a live file, the standard iterator isn't really what you want, as it will terminate as soon as it encounters an EOF marker. To monitor a live file, you either need to periodically poll it for new data, or else set up one of the OS-specific notification mechanisms that lets you know when new data is available.

There are some good answers to that question over here: How do I watch a file for changes?

Writing back to the same file is still going to be a potential problem though, especially if another process has it open for writing (even if you get the concurrent access to work, it will be tricky to get the monitoring program to ignore its own writes without risking missing writes from the application being monitored).

Example of using the file line iterator with collections.deque to remember context information while still benefiting from all the I/O optimisations built into the line iterator:

context = deque(maxlen=10) # Remember most recent 10 lines
for line in f:
    # Process the current line
    context.append(line)
Community
  • 1
  • 1
ncoghlan
  • 40,168
  • 10
  • 71
  • 80
  • Good information hiowever I'm looking for specific content so the line options will not serve my needs. – Surfdork Feb 03 '11 at 00:45
  • If you need to retain some context, you can fairly easily use a deque with "maxlen" set to remember a small number of prior lines. This gives you the benefit of the caching built in to the file iterator without losing the context you need. – ncoghlan Feb 03 '11 at 06:49