search multiple files and copy found text to a new file

Fellow scripters,

This script is what I made/hacked together thanks to this great communiy and its members.

what it’s need to do is this

  • user enters a search string
  • script will search all text files in a certain folder
  • copy all lines from those text files in a new text file and save it on desktop

on run
	
	-- create empty txt file on desktop
	set thedate to (do shell script "/bin/date +%y%m%d_%H%M%S_")
	set filename to thedate & "test.txt"
	tell application "Finder" to make new file at desktop with properties {name:filename}
	
	-- enter search criteria
	set search_criteria to text returned of (display dialog "enter search string" default answer "")
	
	-- look in these files. need to change this to desktop folder which wil hold multiple files
	
	set theFolder to ((path to desktop as text) & "LOGS:")
	tell application "Finder" to set theFiles to files of folder theFolder
	
	repeat with oneFile in theFiles
		
		set somePOSIXFile to oneFile as alias
		set somePOSIXFile to (POSIX path of (somePOSIXFile))
		
		-- read lines of file
		set myfilepath to POSIX file somePOSIXFile as string
		set thedata to paragraphs of (read file myfilepath)
		
		
		-- search in file(s) if string is found
		set searchResult to (do shell script "/usr/bin/grep -q" & space & quoted form of search_criteria & space & quoted form of somePOSIXFile & space & "; echo $?")
		display dialog searchResult as text
		
		--test
		
		-- if found
		if searchResult = "0" then
			
			-- fill text file on desktop with lines with search criteria
			repeat with i in thedata
				if i contains search_criteria then
					
					set thePath to (path to desktop as Unicode text) & filename
					set fileRef to open for access alias thePath with write permission
					write i & (ASCII character 13) to fileRef starting at eof
					
					close access fileRef
					set searchResult to "0"
				end if
			end repeat
			
			
			
			
			-- if not ** need to change this so that this runs only one time ** is now running as much as there are files in the log folder
		else if searchResult = "1" then
			
			try
				tell application "Finder"
					set the file_to_delete to (path to desktop) & filename as string
					delete alias file_to_delete
				end tell
			end try
			
		end if
		
		
	end repeat
end run


however

  • after running the script I can’t save it anymore
  • if i make it an script app it also gives me a safe error even after the whole copy part
  • the part where it delets a file from the desktop runs as much as there are files in the log folder. i know that;s because of the repeat with oneFile in theFiles part but I need it to run only once
  • if the script finds the string in all or the last text files it works fine but it deletes the txt file from the dektop if it doesn’t find it in the last txt file

help!

:slight_smile:

That’s probably because all your variables are “run handler” variables, whose values, like those of properties and globals, are saved back into the script file after it’s run. If your last text is very long, the list of its paragraphs will be too bulky to be saved back into the file with your variable ‘thedata’ and you’ll get an error. The cure is either to set thedata to something brief (perhaps “”) before the script exits or, better still, to declare thedata as local. Even better still, make all the variables local by putting the code in an ordinary handler.

These two are the same phenomenon, of course. To delete the file only under the right circumstances, you’d have to do it after the repeat, depending on the value of some flag variable:

set noMatches to true -- Set this variable to a default value of 'true'.
repeat with oneFile in theFiles

	-- .

	if searchResult = "0" then

		-- Write to the file.

		set noMatches to false -- Change the flag to 'false'.
	end if

end repeat

if (noMatches) then

	-- Delete the file.

end if

But in fact you don’t need to create the file in the first place if there are no matches. The ‘open for access’ command will create it if it doesn’t exist, so there’s no need to have the Finder do it beforehand.

There are more efficient ways to write your script, but this should help with the immediate problems.

Further to the above, here’s one way the script might be written.

on mainHandler()
	
	-- Set name for txt file on desktop
	set thedate to (do shell script "/bin/date +%y%m%d_%H%M%S_")
	set filename to thedate & "test.txt"
	set thePath to (path to desktop as Unicode text) & filename
	
	-- enter search criteria
	set search_criteria to text returned of (display dialog "enter search string" default answer "")
	
	-- Get the log files as aliases.
	set theFolder to ((path to desktop as text) & "LOGS:")
	tell application "Finder"
		try
			set theFiles to (files of folder theFolder) as alias list
		on error
			set theFiles to (first file of folder theFolder) as alias as list
		end try
	end tell
	
	-- Create the file to which to write the matched text and open it for access.
	set fileRef to open for access file thePath with write permission
	
	try -- In case there's an error while the file's open.
		-- Check each log file in turn.
		repeat with oneFile in theFiles
			
			set oneFilePOSIX to (POSIX path of oneFile)
			
			-- search in file(s) if string is found
			try -- In case there's no match in this file.
				set matchedText to (do shell script "/usr/bin/grep" & space & quoted form of search_criteria & space & quoted form of oneFilePOSIX)
				set textMatched to true
			on error number 1
				set textMatched to false
			end try
			
			if (textMatched) then write matchedText & return to fileRef starting at eof
			
		end repeat
	on error errMsg
		display dialog errMsg buttons {"OK"} default button 1 with icon stop
	end try
	
	set noMatches to ((get eof fileRef) = 0) -- Has anything been written to the result file?
	close access fileRef
	
	if (noMatches) then tell application "Finder" to delete file thePath
end mainHandler

mainHandler()

Nigel,

I was still digesting your first post and then I received notification about the 2nd. wow. so fast also. need to go over it line by line and learn from your script.

Noticed that it doesn’t copy only the lines with the search string but everything from the file that the search string is found in.

Will get back to you after detail testing but… WOW!

thanks

The idea was that the “grep” command returns text containg only the paragraphs with the matching text. However, the file I was using has linefeeds rather than returns. I suppose the script may not work if it’s the other way round. Apologies if that’s the case. I’ll take another look later on.

OK. This should work with plain text containing either linefeeds and/or returns, the output being plain text with returns.

on mainHandler()
	
	-- Set name for txt file on desktop
	set thedate to (do shell script "/bin/date +%y%m%d_%H%M%S_")
	set filename to thedate & "test.txt"
	set thePath to (path to desktop as Unicode text) & filename
	
	-- enter search criteria
	set search_criteria to text returned of (display dialog "enter search string" default answer "")
	
	-- Get the log files as aliases.
	set theFolder to ((path to desktop as text) & "LOGS:")
	tell application "Finder"
		try
			set theFiles to (files of folder theFolder) as alias list
		on error
			set theFiles to (first file of folder theFolder) as alias as list
		end try
	end tell
	
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to return
	-- Create the file to which to write the matched text and open it for access.
	set fileRef to open for access file thePath with write permission
	
	try -- In case there's an error while the file's open.
		-- Check each log file in turn.
		repeat with oneFile in theFiles
			
			set txt to (read oneFile)
			
			if (txt contains search_criteria) then
				set txt to txt's paragraphs
				set matchedText to {}
				repeat with i from 1 to (count txt)
					set thisPara to item i of txt
					if (thisPara contains search_criteria) then set end of matchedText to thisPara
				end repeat
				
				write ((matchedText as text) & return) as text to fileRef starting at eof
			end if
			
		end repeat
	on error errMsg
		display dialog errMsg buttons {"OK"} default button 1 with icon stop
	end try
	
	set noMatches to ((get eof fileRef) = 0) -- Has anything been written to the result file?
	close access fileRef
	set AppleScript's text item delimiters to astid
	
	if (noMatches) then tell application "Finder" to delete file thePath
end mainHandler

mainHandler()

again WOW.

need to digest & investigate but it works nicely!

thanks!

KML