How do i iterate through every file including folders?

It only returned folder/files one level deep, and it returned hidden files (mainly .DS_Store).

Here is a recursive version (much neater)

Try this one


on run
	local theFolder
	set theFolder to (choose folder) as text
	set folderTree to getFolderTree(theFolder)
end run

on getFolderTree(theFolder)
	local theFolders, folderTree, theFiles
	set folderTree to (theFolder as text) & linefeed
	set {ATID, text item delimiters} to {text item delimiters, linefeed}
	tell application "System Events"
		set theFiles to (name of every file in folder theFolder whose visible is true)
		sortFolders(theFiles) of me
		if theFiles ≠ {} then set folderTree to folderTree & theFiles as text
		try
			set theFolders to (path of every folder of folder theFolder) --as list
		on error
			tell me to display alert "No folders were found in the selected folder"
			error number -128
		end try
	end tell
	set folderTree to folderTree & linefeed & linefeed
	sortFolders(theFolders)
	repeat with aFolder in theFolders
		set folderTree to folderTree & getFolderTree(contents of aFolder)
	end repeat
	set text item delimiters to ATID
	return folderTree
end getFolderTree

on sortFolders(a)
	repeat with i from (count a) to 2 by -1
		repeat with j from 1 to i - 1
			if item j of a > item (j + 1) of a then
				set {item j of a, item (j + 1) of a} to {item (j + 1) of a, item j of a}
			end if
		end repeat
	end repeat
end sortFolders

I tested the script on my test folder and it worked fine. The timing result was 320 milliseconds.

I was trying my script above with a folder that had over 9000 files.
It took forever and I had to increase the timeout value so that System Events would return the file list.
It seems that the “whose” clause really slows down the result.
If I remove the whose clause, it responded almost instantaneously.
So I decided to parse the list using AppleScript to remove invisible files from the result.
It is much faster!

Also the sort routine was extremely slow, so I replaced it with a combSort which is way faster than the bubble sort that was used before.


on run
	 local theFolder
	 set theFolder to (choose folder) as text
	 set folderTree to getFolderTree(theFolder)
	 saveFolderTree(folderTree)
end run

on getFolderTree(theFolder)
	local theFolders, folderTree, theFiles, visList
	set folderTree to (theFolder as text) & linefeed
	set {ATID, text item delimiters} to {text item delimiters, linefeed}
	try
		tell application "System Events"
			set {theFiles, visList} to {name, visible} of files in folder theFolder --whose visible is true
		end tell
	on error
		return ""
	end try
	repeat with i from (count of theFiles) to 1 by -1
		if item i of visList is false then
			set item i of theFiles to item 1 of theFiles
			set item i of visList to item 1 of visList
			set theFiles to rest of theFiles
			set visList to rest of visList
		end if
	end repeat
	set visList to missing value
	combSort(theFiles)
	if theFiles ≠ {} then set folderTree to folderTree & theFiles as text
	try
		tell application "System Events"
			set {theFolders, visList} to {path, visible} of folders of folder theFolder
		end tell
	on error
		tell me to display alert "No folders were found in the selected folder"
		error number -128
	end try
	repeat with i from (count of theFolders) to 1 by -1
		if item i of visList is false then
			set item i of theFolders to item 1 of theFolders
			set item i of visList to item 1 of visList
			set theFolders to rest of theFolders
			set visList to rest of visList
		end if
	end repeat
	set folderTree to folderTree & linefeed & linefeed
	combSort(theFolders)
	repeat with aFolder in theFolders
		set folderTree to folderTree & getFolderTree(contents of aFolder)
	end repeat
	set text item delimiters to ATID
	return folderTree
end getFolderTree

on saveFolderTree(theText)
	set theFile to (path to desktop as text) & "Folder Contents.txt"
	try
		set fnum to open for access file theFile with write permission
	on error
		display alert "Oops!"
		return
	end try
	set eof fnum to 0
	write theText to fnum starting at 0
	close access fnum
end saveFolderTree

on combSort(alist as list) -- FASTEST
	local i, j, cc, sf, ns, js, gap, pgap, sw -- ns means No Swap
	script mL
		property nlist : alist
	end script
	set sf to 1.5
	set cc to count mL's nlist
	set gap to cc div sf
	repeat until gap = 0
		repeat with i from 1 to gap
			set js to cc - gap
			repeat until js < 1 -- do each gap till nor more swaps
				set ns to gap
				repeat with j from i to js by gap
					if (item j of mL's nlist) > (item (j + gap) of mL's nlist) then
						set sw to (item j of mL's nlist)
						set (item j of mL's nlist) to (item (j + gap) of mL's nlist)
						set (item (j + gap) of mL's nlist) to sw
						set ns to j
					end if
				end repeat
				set js to ns - gap
			end repeat
		end repeat
		set pgap to gap
		set gap to gap div sf
		if gap = 0 then -- no while using as integer
			if pgap ≠ 1 then set gap to 1
		end if
	end repeat
end combSort

If you don’t mind using script libraries, this can be done with Shane’s fileManagerLib.

https://latenightsw.com/freeware/

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"

use script "FileManagerLib" version "2.3.5"
on main()
	set theFolder to POSIX path of (choose folder)
	
	set filesFolders to objects of theFolder ¬
		searching subfolders true ¬
		include invisible items false ¬
		include folders true ¬
		include files true ¬
		result type files list
	set AppleScript's text item delimiters to {return}
	set theText to filesFolders as text
	saveFolderTree(theText)
end main

on saveFolderTree(theText)
	set theFile to (current application's NSHomeDirectory()'s stringByAppendingPathComponent:"Desktop")'s stringByAppendingPathComponent:"Folder Contents.txt"
	(current application's NSString's stringWithString:theText)'s writeToFile:theFile atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
end saveFolderTree

main()

This script is similar to my script in post 4 above, differing in that it only returns file names that contain a specified search string. The script creates a text file on the desktop with the matching files.

use framework "Foundation"
use scripting additions

set sourceFolder to POSIX path of (choose folder with prompt "Select a source folder:" default location (path to home folder))
set searchString to text returned of (display dialog "Enter a search string:" default answer "")
set theFiles to getFiles(sourceFolder, searchString)
if theFiles's |count|() = 0 then display dialog "No file names containing the search string were found." buttons {"OK"} cancel button 1 default button 1 with icon stop
set foldersAndFiles to getFoldersAndFiles(theFiles)
saveFoldersAndFiles(foldersAndFiles, sourceFolder)

on getFiles(sourceFolder, searchString)
	set fileManager to current application's NSFileManager's defaultManager()
	set sourceFolder to current application's |NSURL|'s fileURLWithPath:sourceFolder
	set theKeys to {current application's NSURLIsDirectoryKey, current application's NSURLIsPackageKey}
	set folderContents to (fileManager's enumeratorAtURL:sourceFolder includingPropertiesForKeys:theKeys options:6 errorHandler:(missing value))'s allObjects()
	set thePredicate to current application's NSPredicate's predicateWithFormat_("lastPathComponent CONTAINS[c] %@", searchString)
	set folderContents to folderContents's filteredArrayUsingPredicate:thePredicate
	set theFolders to current application's NSMutableArray's new()
	repeat with anItem in folderContents
		set {theResult, aDirectory} to (anItem's getResourceValue:(reference) forKey:(current application's NSURLIsDirectoryKey) |error|:(missing value))
		if aDirectory as boolean is true then
			set {theResult, aPackage} to (anItem's getResourceValue:(reference) forKey:(current application's NSURLIsPackageKey) |error|:(missing value))
			if aPackage as boolean is false then (theFolders's addObject:anItem)
		end if
	end repeat
	set theFiles to folderContents's mutableCopy()
	theFiles's removeObjectsInArray:theFolders
	set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:"path" ascending:true selector:"localizedStandardCompare:"
	return (theFiles's sortedArrayUsingDescriptors:{theDescriptor})
end getFiles

on getFoldersAndFiles(theFiles)
	set foldersAndFiles to current application's NSMutableArray's new()
	set oldFolder to (theFiles's objectAtIndex:0)'s URLByDeletingLastPathComponent()
	(foldersAndFiles's addObject:(oldFolder as text))
	repeat with aFile in theFiles
		set newFolder to aFile's URLByDeletingLastPathComponent()
		if not (newFolder's isEqual:oldFolder) then (foldersAndFiles's addObject:(linefeed & (newFolder as text)))
		(foldersAndFiles's addObject:(aFile's lastPathComponent()))
		set oldFolder to newFolder's |copy|()
	end repeat
	return foldersAndFiles's componentsJoinedByString:linefeed
end getFoldersAndFiles

on saveFoldersAndFiles(theText, theFolder)
	set theFolder to (current application's NSString's stringWithString:theFolder)'s lastPathComponent()
	set fileName to theFolder's stringByAppendingString:" Folder Contents.txt"
	set theFile to (current application's NSHomeDirectory()'s stringByAppendingPathComponent:"Desktop")'s stringByAppendingPathComponent:fileName
	theText's writeToFile:theFile atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
end saveFoldersAndFiles

In order not to make users guess about your intentions, please be more clear. It is not clear in what form you want to get the hierarchy structure. I will give examples of saving the hierarchy as tabbed text.

I did some clean-up and optimization of my script in post 4. The timing results were 9 milliseconds when run on a folder containing 99 files in 10 folders and 110 milliseconds when run on my home folder.

use framework "Foundation"
use scripting additions

on main()
	set theFolder to POSIX path of (choose folder)
	set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
	try
		set theFolders to getFolders(theFolder)
		set folderTree to getFolderTree(theFolders)
	on error
		display dialog "An error occurred while getting files" buttons {"OK"} cancel button 1 default button 1
	end try
	saveFolderTree(folderTree, theFolder)
end main

on getFolders(theFolder)
	set fileManager to current application's NSFileManager's defaultManager()
	set folderKey to current application's NSURLIsDirectoryKey
	set packageKey to current application's NSURLIsPackageKey
	set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{} options:6 errorHandler:(missing value))'s allObjects()
	set theFolders to current application's NSMutableArray's new()
	(theFolders's addObject:theFolder)
	repeat with anItem in folderContents
		set {theResult, aFolder} to (anItem's getResourceValue:(reference) forKey:folderKey |error|:(missing value))
		if aFolder as boolean is true then
			set {theResult, aPackage} to (anItem's getResourceValue:(reference) forKey:packageKey |error|:(missing value))
			if aPackage as boolean is false then (theFolders's addObject:anItem)
		end if
	end repeat
	set theDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:"path" ascending:true selector:"localizedStandardCompare:"
	return (theFolders's sortedArrayUsingDescriptors:{theDescriptor})
end getFolders

on getFolderTree(theFolders)
	set fileManager to current application's NSFileManager's defaultManager()
	set folderTree to current application's NSMutableArray's new()
	repeat with aFolder in theFolders
		set aFolderPath to ((aFolder's |path|())'s stringByAppendingString:"/")
		(folderTree's addObject:aFolderPath)
		(folderTree's addObjectsFromArray:getFiles(aFolder, theFolders, fileManager))
		(folderTree's addObject:"")
	end repeat
	return (folderTree's componentsJoinedByString:linefeed)
end getFolderTree

on getFiles(theFolder, theFolders, fileManager)
	set theFiles to (fileManager's contentsOfDirectoryAtURL:theFolder includingPropertiesForKeys:{} options:4 |error|:(missing value))'s mutableCopy()
	theFiles's removeObjectsInArray:theFolders
	return ((theFiles's valueForKey:"lastPathComponent")'s sortedArrayUsingSelector:"localizedStandardCompare:")
end getFiles

on saveFolderTree(theString, theFolder)
	set fileName to (theFolder's lastPathComponent)'s stringByAppendingString:" Folder Contents.txt"
	set theFile to (current application's NSHomeDirectory()'s stringByAppendingPathComponent:"Desktop")'s stringByAppendingPathComponent:fileName
	(current application's NSString's stringWithString:theString)'s writeToFile:theFile atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
end saveFolderTree

main()

I rewrote the above script utilizing a somewhat different approach. I thought it might be faster, but it was actually about 10 percent slower. It does not return empty folders and might be considered for this reason.

use framework "Foundation"
use scripting additions

on main()
	set theFolder to POSIX path of (choose folder)
	set theFiles to getFiles(theFolder)
	if theFiles's |count|() is 0 then display dialog "No files found" buttons {"OK"} cancel button 1 default button 1
	set folderTree to getFolderTree(theFiles)
	saveFolderTree(folderTree, theFolder)
end main

on getFiles(theFolder)
	set fileManager to current application's NSFileManager's defaultManager()
	set theFolder to current application's |NSURL|'s fileURLWithPath:theFolder
	set directoryKey to current application's NSURLIsDirectoryKey
	set packageKey to current application's NSURLIsPackageKey
	set folderContents to (fileManager's enumeratorAtURL:theFolder includingPropertiesForKeys:{} options:6 errorHandler:(missing value))'s allObjects() --option 6 skips hidden files and package contents
	set theFolders to current application's NSMutableArray's new()
	set booleanTrue to current application's NSNumber's numberWithBool:true
	repeat with anItem in folderContents
		set {theResult, aDirectory} to (anItem's getResourceValue:(reference) forKey:directoryKey |error|:(missing value))
		if aDirectory is booleanTrue then
			set {theResult, aPackage} to (anItem's getResourceValue:(reference) forKey:packageKey |error|:(missing value))
			if not (aPackage is booleanTrue) then (theFolders's addObject:anItem)
		end if
	end repeat
	set theFiles to folderContents's mutableCopy()
	theFiles's removeObjectsInArray:theFolders
	set pathDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:"stringByDeletingLastPathComponent" ascending:true selector:"localizedStandardCompare:"
	set nameDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:"lastPathComponent" ascending:true selector:"localizedStandardCompare:"
	return (theFiles's valueForKey:"path")'s sortedArrayUsingDescriptors:{pathDescriptor, nameDescriptor}
end getFiles

on getFolderTree(theFiles)
	set fileTree to current application's NSMutableArray's new()
	set previousFolder to (theFiles's objectAtIndex:0)'s stringByDeletingLastPathComponent()
	fileTree's addObject:(previousFolder's stringByAppendingString:"/")
	repeat with aFile in theFiles
		set aFolder to aFile's stringByDeletingLastPathComponent()
		set aFileName to aFile's lastPathComponent()
		if (aFolder's isEqualToString:previousFolder) is false then
			(fileTree's addObject:"")
			(fileTree's addObject:(aFolder's stringByAppendingString:"/"))
			(fileTree's addObject:aFileName)
		else
			(fileTree's addObject:aFileName)
		end if
		set previousFolder to aFolder
	end repeat
	return fileTree
end getFolderTree

on saveFolderTree(theString, theFolder)
	set theString to theString's componentsJoinedByString:linefeed
	set fileName to ((current application's NSString's stringWithString:theFolder)'s lastPathComponent())'s stringByAppendingString:" Folder Contents.txt"
	set theFile to ((current application's NSHomeDirectory())'s stringByAppendingPathComponent:"Desktop")'s stringByAppendingPathComponent:fileName
	(current application's NSString's stringWithString:theString)'s writeToFile:theFile atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
end saveFolderTree

main()