Read a folder structure

Hi, my background is knowledgeable about other programming languages, unitiated with AppleScript.

Anyway, my current task is to read a folder structure without knowing what folder will be in it, nor what folders will have inside of them. It seems that get the entire contents of a folder will return every folder or file nestled within the folder, but as a flat list. I would like a list of lists, each list representing a folder, and a reference to a file at the “end” of the branching in the “final” list(s).

It seems that recursive functions are the way to go.

My first question is “Is that correct?”

My second question, assuming that the answer to the first is “Yes,” is what is the proper way to do this? I seem to get 70% of the way there then something goes kaput. I’m sure this is just my lack of experience in the AppleScript language, but I’ve also have not had good luck finding answers on the Internets.

Here’s the basic idea of what I’ve got so far…

set thePath to "/Users/dru/Library/Preferences/Macromedia/Flash Player/#SharedObjects/5CARVELE/"
set theMacPath to POSIX file thePath

tell application "Finder"
	set theFolders to my walkFolderStructure(folder theMacPath)
	log theFolders
end tell


on walkFolderStructure(theFolder)
	set theList to {}
	repeat with f in every item in theFolder as list
		set aFolder to walkFolderStructure(f)
		copy aFolder to the end of theList
	end repeat
	return theList
end walkFolderStructure

I seem to get odd results, though. Empty values in a list, or just “item 1”, etc. I’m obviously missing a key element or three.

Incidentally, I’ll throw this out there in case it means something. I’m intending for this folder structure list to be brought into an AS Studio application. Basically I’m trying to build a file browser for a very specific folder (The location of the SharedObects for the Flash Player, in case you’re curious). Ultimately I’d like to browse the folders and files in an Outline View, and choose to view a file’s contents after browsing around selecting a file.

So basically I want to create a datasource out a folder structure. If I’m going about this completely the wrong way, please tell me now.

Hi,

there is a quite easy way to get a whole folder structure of an folder in AppleScript:
entire contents considers all folders and subfolders

set theMacPath to ((path to preferences folder) as string) & "Macromedia:Flash Player:#SharedObjects:"
tell application "Finder" to set theFolders to every folder of entire contents of folder theMacPath

Thanks, Stefan, but I haven’t had good luck with that. Correct me if I’m wrong, but that returns a flat list, yes? It seems that when I tried that, I ended up with a one-dimensional list that did contain every subfolder within the main one, but I lost any sense of hierarchy.

What I’d like to do is maintain the folder structure so that it can me mirrored in an Outline View, you know, if folder B is inside folder A, I want the line for folder A to have a disclosure triangle that reveals/hides folder B. With “entire contents” I lose the fact that folder B is inside folder A.

I think, though, that the words “every folder of contents” got me a little closer. If anyone has more input, I’m all ears. I’m still not sure what the best way to go about this is (including the ultimate goal of using this as a datasource for an Outline View).

And the “path to preferences folder” was a nice bonus tip, thanks for that! I assume thats a value that’s different depending on the machine and current User, but will give me the correct path to the preferences folder no matter who’s using the script?

Thanks…

I think StefanK was giving you something to add to you script rather than the whole script.

global theFolders
set theMacPath to ((path to preferences folder) as string) & "Macromedia:Flash Player:#SharedObjects:"
tell application "Finder" to set thecontents to entire contents of folder theMacPath
tell application "Finder" to set theFolders to every folder of entire contents of folder theMacPath
set Folderscount to count of items in theFolders

my walkFolderStructure()

on walkFolderStructure()
	
	repeat with i from 1 to number of items in theFolders
		set this_item to item i of theFolders
		try
			tell application "Finder" to set bFolders to every folder of entire contents of this_item
			
			tell application "Finder" to set aitems to entire contents of this_item
		end try
	end repeat
end walkFolderStructure

Hi drukepple,

According to your first script with the recursive handler
I wrote a example to show the folder structure of a folder in a TextEdit document,
subfolders are indented with tabs.

global tList, n
set {n, tList} to {0, ""}
get_folder(choose folder, n)

on get_folder(fo, n)
	tell application "Finder" to set a to (get folders of fo)
	if (count a) > 0 then
		set n to n + 1
		repeat with i in a
			set tList to tList & tabs(n) & (get name of i) & return
			get_folder(i, n)
		end repeat
	else
		set n to n - 1
	end if
end get_folder

tell application "TextEdit"
	make new document with properties {text:tList}
end tell

on tabs(n)
	set t to ""
	repeat n times
		set t to t & tab
	end repeat
	return t
end tabs

hi drukepple,

maybe something like this?(It’s building a list of lists of records of all dirs and subdirs of a selected directory - without recursion):

set fld to (POSIX path of (choose folder))
set subfld_list to paragraphs of (do shell script "find " & (quoted form of fld) & " -type d | sed 's|^" & fld & "/||'")
-- the sed may be an error source when there are path names conflicting with sed's regexp part - so the above line might need some bug fix

set {od, text item delimiters} to {text item delimiters, "/"}

set theList to {}
set lastGenerationCount to 1
set subfld_list to items 2 thru -1 of subfld_list
repeat with subfld in subfld_list
	set subfld to subfld as text
	set generationCount to (count of text items of subfld)
	if (generationCount is 1) then
		copy {dirName:subfld as text, fullPath:fld & subfld, dirContent:{}} to the end of theList
		set lastDir to item -1 of theList
		
	else if (generationCount > lastGenerationCount) then
		set thisDirName to text item -1 of subfld
		copy {dirName:thisDirName, fullPath:fld & subfld, dirContent:{}} to the end of (dirContent of lastDir)
		set sameDir to lastDir
		set lastDir to item -1 of dirContent of lastDir
	else
		set thisDirName to text item -1 of subfld
		copy {dirName:thisDirName, fullPath:fld & subfld, dirContent:{}} to the end of (dirContent of sameDir)
	end if
	set lastGenerationCount to generationCount
end repeat
set text item delimiters to od
get theList

Thanks, Dominik, I think that’s getting closer. I’ll take a look at that tonight, hopefully (I’m at work now, so I can’t really spend time playing with my little AS Studio project right now).

Interesting, though…is there a reason you were avoiding recursion? My original post may have been misleading. I don’t have anything against recursion. I’m happy to do it.

But I think the end result is more what I’m after. I’ll need to get actual files as well, but this large, multi-dimensional record is, I think, what I’m going to need to use as a data source for an Outline View.

I thought a recursive AppleScript solution might be slower than walking the results of the quite fast find command - that’s the only reason why …

Here a second version - it’s including files this time:

set fld to (POSIX path of (choose folder))
set subfld_list to paragraphs of (do shell script "find " & (quoted form of fld) & " -type d | sed 's|^" & fld & "/||'")
-- the sed may be an error source when there are path names conflicting with sed's regexp part - so the above line might need some bug fix

set {od, text item delimiters} to {text item delimiters, "/"}

set theList to {}
set lastGenerationCount to 1
set subfld_list to items 2 thru -1 of subfld_list
repeat with subfld in subfld_list
	set subfld to subfld as text
	set generationCount to (count of text items of subfld)
	if (generationCount is 1) then
		copy {dirName:subfld as text, fullPath:fld & subfld, dirContent:my filesListOfFolder(fld & subfld)} to the end of theList
		set lastDir to item -1 of theList
		
	else if (generationCount > lastGenerationCount) then
		set thisDirName to text item -1 of subfld
		copy {dirName:thisDirName, fullPath:fld & subfld, dirContent:my filesListOfFolder(fld & subfld)} to the end of (dirContent of lastDir)
		set sameDir to lastDir
		set lastDir to item -1 of dirContent of lastDir
	else
		set thisDirName to text item -1 of subfld
		copy {dirName:thisDirName, fullPath:fld & subfld, dirContent:{}} to the end of (dirContent of sameDir)
	end if
	set lastGenerationCount to generationCount
end repeat
set text item delimiters to od
get theList

on filesListOfFolder(theFolder)
	try
		tell application "System Events" to return name of files of folder (theFolder as POSIX file as string) whose package folder is false
	on error
		return {}
	end try
end filesListOfFolder

These instructions return a fairly nice list digging down:

set fld to quoted form of (POSIX path of (choose folder))
set C to paragraphs of (do shell script "ls -FR " & fld)

The first block (before an empty paragraph) contains the files and folders at the top level. The folder names are followed by a slash.

Thereafter, the lead item in each block (between empty paragraphs) is the path to an internal folder followed by its contents.

Hi,

Firstly, if you need AppleScript references in the end, it’s best to get the AppleScript type references instead of posix (unix) type references and converting to AppleScript references.

If you need the names of items for an outline view, then you need to create parent and child items if I remember right. You can’t just use a list of lists as a datasource, as you can in a table view. Another thing is that if you use an outline view and you want the arrow marker for empty folders, then you need to ‘call method’ to show the marker for empty folders. If you use a browser view instead, you can show that an item is a folder with the leaf property even though it is empty. I think this is right as I remember.

First, I would check out the method to show an empty parent. Iwould see if I want to do that. Otherwise, I would use a browser view.

gl,

Forgot entirely about the browser view, Kel. Refresh my memory on how to get one.

Never mind - I just found out. :slight_smile:

Hi,

I was thinking about it and maybe you can use a list of lists as a data source for outlines. It’s been a while since I made one. I’m trying again right now.

Later,

Does anyone know how to mirror a directory through creating files?

I figured out how to make a new file:

set outputPath to "Users/interfaced/Desktop/Articles/testscript/"
do shell script " touch " & quoted form of POSIX path of outputPath & "file.gif"

but I need to be able to mirror subdirectories and files. It’s like what you guys were doing before, but instead of creating a string, it’s creating a directory structure and 0kb files.

Thanks for your help!

You mean copy/duplicate a folder?

Not sure what you’re trying to do.

Oh well then touch does the job.

Let me explain what I am trying to do.

I am analyzing people’s organizational habits. I want to go to an office or send around this script so that I can copy the file structures and file names+extensions for later analysis. I don’t want to copy the files, just the names and extensions and subdirectories.

Thanks for the quick response!

Jacques;

I get an “unterminated quote” message when I try this, but it does fill the destination folder with the correct folder structure.

Thank you soooooo much Jacques!!

I can’t believe how quick and easy this is! I really need to learn applescript better

Thank you