Change List Repeat Loop Is Processing

Hello,

I’m new to MacScripter and AppleScript in general and have been working on a small script. I have been searching on and off for weeks trying to find an answer to my problem but have not come across anything similar. Perhaps I am not searching for the correct terms or there is a much simpler way to do this.

My goal is to check if the ‘fileName’ passed to the script contains any of the text strings ‘phrases’ contained in each paragraph of a text file. I can get this to work using a repeat loop if I only use one text file.

However, I have a handful of text files in a folder that I want the script to check against. My thought was that if the fileName did not contain one of the phrases from the first text file I could increment variable ‘type’ and pass that to a handler to return the name and list of paragraphs from the next text file in the folder list.

I can return the correct variable and list from the handler but I do not understand or know if you can change the list the repeat loop is checking against.



set theFilePath to theFile as text

-- Remove file path
set AppleScript's text item delimiters to "/"
set fullName to last item of text items of theFilePath
set AppleScript's text item delimiters to {""}

-- Remove extension
set AppleScript's text item delimiters to "."
set fileName to first item of text items of fullName
set AppleScript's text item delimiters to {""}

tell application "Finder"
	set cList to displayed name of every file of entire contents of alias "Users:jfisher:SynologyDrive:#Hazel:Library:Text Files:" as list
end tell

on changeType(type)
	-- Access global list 'cType'
	global cList
	
	if type is less than or equal to (count cList) then
		set cType to item type of cList
		set txtPath to "Users:jfisher:SynologyDrive:#Hazel:Library:Text Files:" & cType & ".txt"
		set matchList to paragraphs of (read alias txtPath) as list
		return {{cType}, {matchList}}
	end if
end changeType

set type to 1

set criteria to item 2 of changeType(type)

repeat with phrase in criteria
	if fileName does not contain phrase then 
		set type to type + 1
		display dialog type
		set criteria to item 2 of changeType(type)
	else
		return {hazelPassesScript:true, hazelOutputAttributes:{cType}}
	end if
end repeat

Hi. Welcome to MacScripter.

I can’t tell from your script whether you want to return the first matching paragraph found, every match, just the fact that a match exists, or something else. Taking a guess, I’ve come up with the following. It returns the first matching paragraph found if there is one, otherwise it returns a result indicating no match. It should get you started, but do ask again if you can’t adapt it for your actual needs!

set theFilePath to theFile as text

-- Remove file path
set AppleScript's text item delimiters to "/"
set fullName to last text item of theFilePath
set AppleScript's text item delimiters to {""}

-- Remove extension
set AppleScript's text item delimiters to "."
set fileName to first text item of fullName
set AppleScript's text item delimiters to {""}

checkName(fileName)

on checkName(fileName)
	-- Get a list of Finder references to the text files, sorted by name.
	-- For the moment, I've assumed that the "Text files" folder only contains the relevant text files, not in subfolders, and numbered as described in the post #1.
	tell application "Finder" to set cList to (sort every file of folder "SynologyDrive:#Hazel:Library:Text Files:" of home by name)
	
	-- Work through each text file in turn.
	repeat with textFile in cList
		-- Coerce each Finder reference to alias, read the corresponding text file, and extract the paragraphs.
		set textFileAlias to textFile as alias
		set matchList to paragraphs of (read textFileAlias as «class utf8») -- Assuming the text in the file is encoded as UTF-8.
		
		-- Work through the paragraphs and see if any of them contain the file name.
		repeat with i from 1 to (count matchList)
			set phrase to item i of matchList
			-- If any do, return the hit immediately.
			if (fileName contains phrase) then return {hazelPassesScript:true, hazelOutputAttributes:{phrase}} -- Guessing that 'phrase' = 'cType'.
		end repeat
	end repeat
	
	-- If no hits above, return a 'false' result.
	return {hazelPassesScript:false, hazelOutputAttributes:{}}
end checkName

Wow thank you for the quick response.

I think I understand what is going on but will have to take a closer look tonight to make sure haha. After my initial post I had thought of better way to describe my problem so thought it might help if someone else is looking for a similar answer in the future.

For example, I have multiple text files stored in a folder somewhere , each with the brand name of a vehicle manufacturer as their file name. Each text file would contain relevant vehicle models on each line of the text file.

Now if I pass in a file named F-150.jpg or Mustang.jpg the script will use the paragraphs of the first text file named ‘Dodge’ for instance as its matching criteria. The loop will not find any matching paragraph. The repeat loop would then need to switch to the next text file ‘Ford’ where it will find a match. The loop would then return the name of the text file it found the matching paragraph in to use in return {hazelPassesScript:true, hazelOutputAttributes(BrandName)}, unless it cannot find any match at all and returns {hazelPassesScript:false}.

Hopefully that is a little more clear.

Got a chance to run your script. It appears to work well however I would like to actually return the name of the text file that is being processed when a match against one of its paragraphs occurs not the actual phrase that matched.

I believe it would be textFileAlias but textFileAlias returns the path to the file and not its name. Is there a cleaner way to return only the name of the file instead of using text delimiters to clean it up?

Generally, it depends on what you’ve got, what you want, how many times you need to get one from the other, and what you know about the machine(s) running the script. :slight_smile:

Here, textFileAlias is an alias specifier and you want the name of the item to which it refers — without the “.txt” extension, to judge from your original script. If you know that the script will only ever be run on machines where the users’ Finder preferences are set not to “show all filename extensions”, you might get away with changing this line in my script …

if (fileName contains phrase) then return {hazelPassesScript:true, hazelOutputAttributes:{phrase}} -- Guessing that 'phrase' = 'cType'.

… to this :

if (fileName contains phrase) then
	tell application "Finder" to set cType to displayed name of textFileAlias
	return {hazelPassesScript:true, hazelOutputAttributes:{cType}}
end if

But otherwise the fastest and safest way would be to replace it with something like this:

if (fileName contains phrase) then
	set textFilePath to textFileAlias as text
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ":"
	-- Extract the file name from the HFS path. We can assume that the path to a readable text file doesn't end with a colon.
	set cType to text item -1 of textFilePath
	-- Drop the name extension if there is one, bearing in mind that names can contain more than one dot.
	if (cType contains ".") then
		set AppleScript's text item delimiters to "."
		set cType to text 1 thru text item -2 of cType
	end if
	set AppleScript's text item delimiters to astid
	
	return {hazelPassesScript:true, hazelOutputAttributes:{cType}}
end if

Nigel,

I used the second suggestion you posted and the script appears to work great. Just as intended. I appreciate all the help.

Nigel,

I have been working with this script in Hazel but seem to keep getting a Hazel GUI error promp “Error Executing Applescript (null)” on basically ever external AppleScript I try to run through Hazel.

I started to look at it a bit manually inside the AppleScript Editor and noticed that I get error -10004 every time application “System Events” tries to read one of the text files.

From the “Event/Replies” dialog it looks like Tell block for “System Events” to read the text file throws the error and then next line tell “current application” reads the file without issue. It looks like it is being read twice but that could also be my lack of understanding.

Last, to improve the filtering of my files even further I made a copy of the original script and pointed it at a different set of text files. I then used both scripts inside one Hazel rule to output additional information so rename the files. However, I noticed that if I use both scripts inside one rule the first will run and return the correct information but the second script will only return the name of one of the text files regardless of if it is correct or not. It is not even the first text file in the list but if the script is run manually it returns the correct value.

That sounds like you’re using the read command from Standard Additions inside the System Events tell block. You shouldn’t do that – the read command should be used outside any application tell blocks (or in their own tell current application blocks).

As it stands, this generates an error, as you’re seeing, and then AppleScript tries again,this time redirecting the command to current application. That’s only a bit inefficient – the file isn’t actually read the first time – but it’s possible at some stage the redirection will be removed. So it’s probably better to restructure the code accordingly.