Archive: Searching for a string in a text file


Searching for a string in a text file
I'm using stu's FileSearch function to search for a string, but it isn't working. The string is certainly in the file, but it keeps looping. Ideas? Also, does $1 indicate the string was found, or the file was found?

CheckForCircuit:
Sleep 5000
Push $EXEDIR\temp\tor.log
Push "Successfully created a circuit"
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in
StrCmp $1 yes LaunchFF CheckForCircuit


$1 indicates the string was found.
Ran it 50 times or so, with sort string, long string, long string with spaces, always fool-proof. :-)


I'll check it out when I can get on my build machine.

-Stu


Could you post tor.log?
Function works fine for me.

-Stu


Here is the file


perhaps a better explanation is needed.

execdos is writing this tor.exe's output to the log file. And the file keeps getting updated. That is why I have a 5000 delay. Is it not getting read because the file is already open and being appended to?


Uh, first of all, in the log file you attached there's no such string you were looking for. So no surprise it loops.

And second, to see if the file was opened successfully, in FileSearch function after the FileOpen command add
IfErrors 0 +2
MessageBox MB_OK "FileOpen error!"


galil
yes, i'm aware that isn't the string. i just typed that in. infact it doesn't even find "circuit".

However you are right. There is a fileopen error! So if it is already open and being appended, what do you suggest? Is there a way to read a file that is already opened and being written to by another program?


check out this example, handles the fileopen case, though, we stop the execution at certain n , because we read and write in a loop. Assuming that other program writes in the file we read the execution is terminated once the file no more updated.
* there is a strange n of lines shown but I guess it is because of the read/write loop. Anyway the string and n of times found is fool-proof. :-)

!define STRING 'Successfully created a circuit'
!define FILE '$EXEDIR\install.log'
!define TEMPFILE '$TEMP\tmp.log'

outfile 'test.exe'
ShowInstDetails show

section -
strcpy $R1 0 ; needed to stop loop for the example
_loop:
FileOpen $R0 '${FILE}' a
FileSeek $R0 0 END
FileWrite $R0 'bla bla bla$\r$\n'
FileWrite $R0 '${STRING}$\r$\n'
FileWrite $R0 'bla bla bla$\r$\n'
; this is only for to stop loop for the example
; in real world the prog who writes on readme.txt
; when is about to finish will write the last record EOF
; so when the prog who reads the tmp.log should stop loop
Intop $R1 $R1 + 1
intcmp $R1 '9' +2 +2 +1
FileWrite $R0 'EOF$\r$\n'
fileclose $R0
CopyFiles '${FILE}' '${TEMPFILE}'

Push '${TEMPFILE}'
Push '${STRING}'
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in

StrCmp $1 yes 0 _end
DetailPrint "'${STRING}' was found in the file $0 times on $2 lines."
Push '${TEMPFILE}'
Push 'EOF'
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in
StrCmp $1 yes _end
goto _loop
_end:
delete ${TEMPFILE}
sectionend
don't forget Stu's function search for text in file

* this article entirely edited in order to make more sense. (hopful) :-)

I did try to make my comments more readable and upload here the entire script including Stu's function.
@ Stu I hope you don't mind. If you do, just remove the link :-)


The problem i'm having is the FileOpen fails. If FileOpen didn't work in stu's FileSearch, I don't think it will work here either.


the above script is full working. why don't you try to customize it according what you want to do?


Stu's script works fine as well. The problem has nothing to do with the script, it is that the file i am trying to open is already opened by execdos and thus locked.


If another process has the file locked then make a copy of it and search that instead (like Red Wine's example shows). It may or may not work but it's worth trying.

-Stu


I tried copying it a while back. It ends up writing a file fragment.

If it helps the issue, the file that i'm tring to open is originally opened (thus locked?) by ExecDos as it's log file:

ExecDos::exec /NOUNLOAD /ASYNC `"$EXEDIR\tor\tor.exe" -f "$EXEDIR\tor\torrc"` "" "$EXEDIR\temp\tor.log"

So it is inside my program, if i can figure out how to access it. Afrow, any ideas about using a variable as the log file and having that variable written to a file i can open?


I got the point a little late :-)
Anyway I don't know how the execdos log works but when I'm testing those below they work fine. The problem is that must exists a keyword to the end of the logging. The keyword could be the same with the one you're looking for if you just want to find it one time. If you want to find it n times then must exists a keyword that means end of logging.
When I run loop_read begins looping, then I run loop_write and they both work fine all the way to the end. For the example I run the logging for specific rounds, and I use as keyword EOF that tells me the logging is finished so terminate the loop_read.

!define STRING 'Successfully created a circuit'
!define FILE '$EXEDIR\install.log'

outfile 'loop_read.exe'
ShowInstDetails show

section -
_loop:
Push '${FILE}'
Push '${STRING}'
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in

StrCmp $1 yes 0 _loop
DetailPrint "'${STRING}' was found in the file $0 times"

Push '${FILE}'
Push 'EOF'
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in
StrCmp $1 yes _end
goto _loop
_end:
sectionend

!define FILE '$EXEDIR\install.log'
!define STRING 'Successfully created a circuit'

outfile 'loop_write.exe'
showinstdetails show

section -
FileOpen $R0 '${FILE}' a
_loop:
FileSeek $R0 0 END
FileWrite $R0 'bla bla bla$\r$\n'
FileWrite $R0 'bla bla bla$\r$\n'
FileWrite $R0 'bla bla bla$\r$\n'
FileWrite $R0 'bla bla bla$\r$\n'

Intop $R1 $R1 + 1
intcmp $R1 '199' +4 +4 +1
FileWrite $R0 '${STRING}$\r$\n'
FileWrite $R0 'EOF$\r$\n'
goto _end
goto _loop
_end:
fileclose $R0
sectionend

It's a shame ExecDos doesn't support ExecToStack (or similar) like nsExec. Ideally it would push a line of output to the stack as an individual stack item which you could Pop in a loop.

-Stu


Okay. This is now confusing. I found out the file appears to be share read access supposedly. Here is the code inside execdos:

/* open log file if name is correct */
if(*(ptp->logFile) != 0 &&
(f = CreateFile(ptp->logFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{ rslt = ERR_LOGOPEN; break; }


But yet, I cannot use CopyFiles to copy it. That doesn't seem right.

Afrow, instead of using ExecDos to generate the log file, I can have the external program pipe the data to stdout. Can stdout be captured somehow?


SOLUTION FOUND.

If I copy the file, I get a file fragment. And this would normally be no good, except that the reason I think it is a file fragment is because it has no EOF. But that doesn't matter. If I just ignore that it is a fragment and read the file, it totally works. However, I am concerned about how other OSes and file systems will interpret the fragment. Thoughts on this?

So here is the code for all eternity:

LaunchTor:
SetOutPath "$EXEDIR"
ExecDos::exec /NOUNLOAD /ASYNC `"$EXEDIR\tor\tor.exe" -f "$EXEDIR\tor\torrc"` "" "$EXEDIR\temp\tor.log"
Sleep 2000
FindProcDLL::FindProc "tor.exe"
Pop $R0
StrCmp $R0 "0" "" CheckForCircuit ; If tor.exe process is not running, start tor again, else goto LaunchFF
Goto LaunchTor

CheckForCircuit:
Sleep 1000
CopyFiles $EXEDIR\temp\tor.log $EXEDIR\temp\tor.chk
Push $EXEDIR\temp\tor.chk
Push "Tor has successfully opened a circuit"
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in
Delete $EXEDIR\temp\tor.chk
StrCmp $1 yes LaunchFF CheckForCircuit


unless I'm still out of the point, I made it as simple as gets following the above.
I made a bat file (displays the nsis license), run it with execdos and looking for string "Altered source versions", so everything works fine at this point. even if you put a pause at the end of the bat loop_read keeps looping untill you kill cmd.exe with task manager, then terminates with success.
the bat:

@echo OFF
rem name it tor.bat
ECHO Copyright (C) 1999-2006 Nullsoft, Inc.
ECHO.
ECHO This license applies to everything in the NSIS package,
ECHO except where otherwise noted.
ECHO.
ECHO This software is provided 'as-is', without any express or implied warranty.
ECHO In no event will the authors be held liable for any damages arising from
ECHO the use of this software.
ECHO.
ECHO Permission is granted to anyone to use this software for any purpose, including
ECHO commercial applications, and to alter it and redistribute it freely, subject
ECHO to the following restrictions:
ECHO.
ECHO 1. The origin of this software must not be misrepresented;
ECHO you must not claim that you wrote the original software.
ECHO If you use this software in a product, an acknowledgment in the product
ECHO documentation would be appreciated but is not required.
ECHO.
ECHO 2. Altered source versions must be plainly marked as such,
ECHO and must not be misrepresented as being the original software.
ECHO.
ECHO 3. This notice may not be removed or altered from any source distribution.
rem pause

the script:
!define STRING 'Altered source versions'
!define FILE '$EXEDIR\tor.log'

outfile 'loop_read.exe'
ShowInstDetails show

section -
ExecDos::exec /NOUNLOAD /ASYNC `"$EXEDIR\tor.bat" -f "$EXEDIR\torrc"` "" "$EXEDIR\tor.log"
_loop:
Push '${FILE}'
Push '${STRING}'
Call FileSearch
Pop $0 #Number of times found throughout
Pop $1 #Found at all? yes/no
Pop $2 #Number of lines found in

StrCmp $1 yes 0 _loop
DetailPrint "'${STRING}' was found in the file $0 times"
sectionend

I think you may be missing the point Red Wine.
ExecDos keeps the log file in use while it writes to it (/ASYNC) and while it is in use, we can't access it from elsewhere (i.e. in the NSIS script).
/ASYNC means that it runs as a seperate thread along side our NSIS script.
For torpark your code will infinately loop until ExecDos has finished.

-Stu


So, we need a sync access to the log, it's useless if we read it after is created. Am I right?
I thought we just need to know if tor 'Successfully created a circuit' in order to launch FF, else re-launch tor or do some other action.


Yes. Probably needs to find out when part of the process is complete so that another task can be executed (or perhaps the task can be closed).

-Stu