Creating a plain text file of directories and sub-directories

This has got to be simplier than what I am trying to use. I would like to create a document, plain text file, with a list of all files and their enclosing folders, tab-delimited if possible, but I’d take anything right now.

I have hacked together bits of other scripts and stepped through this, but am getting an error that the destination file is ‘already open’ even when it is not. There are several similar samples from the brightest among those here – yet I just can’t seem to find a way to get the file to be written.

Code sample:

global fileList
on listFolder(f, s)
list folder f
repeat with i in the result
if folder of (info for alias (f & i)) then
set fileList to fileList & s & i & “(f)” & return
listFolder(f & i & “:”, s & "� � ")
else
set fileList to fileList & s & i & return
end if
end repeat
end listFolder

set fileList to “”
set theFolder to (choose folder “Select a folder to list :”) as string
listFolder(theFolder, “”)

set listFile to (theFolder & “Folder Listing.txt”)
write fileList to listFile
close access file listFile

I had high dreams of writing the data into a spreadsheet, but just a simple way to make an index of filenames and their relative locations in a text file can’t be this dang hard.

Can someone whack me with the intellistick so I can see what I’m doing wrong?

Here’s what I’m using:


	set m to choose file
	set output to open for access m with write permission
	--repeat with i from 1 to count of listFiles
	set elem to item 1 of listFiles
	set strLine to file_name of elem & "	" & file_modified of elem & "	" & file_size of elem & "	" & file_kind of elem & "	" & file_comments of elem & return
	set strLine to strLine & getSubItems(elem, strLine)
	write strLine to output
	--end repeat
	close access output

Hi,

When writing to file you should use error checking in there because if you make an error the file will remain open even if it doesn’t look like it’s open and it is a bit of a hassle, especially if you are just learning, to close the file. If you make this mistake of not closing the file, type in the Script Editor ‘close access’ path to the file and run it.

Later,

That’s a pretty handy script! :slight_smile: As Kel’s suggested, you may have been having problems because the file hadn’t been closed on a previous run of the script. You can close files that have accidentally been left open in these cases, I believe, by quitting the application running the script.

Here’s a slight reworking with safer file handling code. I’ve also replaced ‘folder of info for’ with something a little faster.

global fileList

on listFolder(f, s)
	list folder alias f
	repeat with i in the result
		set fp to (f & i) as alias as string
		considering case
			if fp ends with ":" then
				set fileList to fileList & s & i & "(f)" & return
				listFolder(fp, s & "    ")
			else
				set fileList to fileList & s & i & return
			end if
		end considering
	end repeat
end listFolder

set theFolder to (choose folder "Select a folder to list :") as string
listFolder(theFolder, "")

set listFile to (theFolder & "Folder Listing.txt")
set fRef to (open for access file listFile with write permission)
try
	set eof fRef to 0
	write fileList to fRef
end try
close access fRef
set fileList to ""

Nigel and everyone else: thank you (!) for all of your assistance. This code comes close to what I am trying to accomplish, but oddly enough, I still get errors. The first error was an initially undefined fileList variable, but that was easily solved. What is more puzzling is trying to format the output for easy import into a spreadsheet. Creating a delimited list of folder:filename (or folder:subfolder:…:filename reports an undefined ‘foldertemp’ variable. I would have thought my code below would have defined the ‘foldertemp’ variable initially, and updated it properly int he conditional to be used in generating the fileList variable.

Aren’t such temporary variables retained during the execution of such loop structures? Here’s the errant code:

set fileList to “”
set foldertemp to “”
set carriage_return to return as text
set message_A to “This script will generate a list of all folder-filename.ext pairs into a text file called Folder Listing.txt” & carriage_return & carriage_return & “Select the folder to catalog (and let the script run).”
display dialog message_A buttons {“OK”} default button 1 with icon note
global fileList

on listFolder(f, s)
list folder alias f
repeat with i in the result
set fp to (f & i) as alias as string
considering case
if fp ends with “:” then
set foldertemp to fileList & s & i
else
set fileList to foldertemp & “:” & fileList & s & i & return
end if
end considering
end repeat
end listFolder

set theFolder to (choose folder “Select a folder to list :”) as string
listFolder(theFolder, “”)

set listFile to (theFolder & “Folder Listing.txt”)
set fRef to (open for access file listFile with write permission)
try
set eof fRef to 0
write fileList to fRef
end try
close access fRef
set fileList to “”

Many thanks again!

p

I have forced myself into the habit of trying to close a file before I do any writing.

try
  close access fRef 
end
try 
  set eof fRef to 0 
  write fileList to fRef 
end try 
close access fRef 

It doesn’t hurt anything to try and close it (as far as I know), but it sure helps to avoid problems later. I don’t know if this helps with your problem specifically, though.

The “FolderTemp” will net be set unless it meets a true statement of the “if”.


if fp ends with ":" then
				set foldertemp to fileList & s & i
			else
				set fileList to foldertemp & ":" & fileList & s & i & return
			end if

So in this case if the “fp” ends with “:” then the foldertemp will be set, otherwise it will be what you set for a property. So when the condition is not met, “else”, the code then tries to set the fileList to “[empty]” & “:” & fileList & s & i & return
So set the foldertemp to a proper value and then it will work.

Hi, afterhours.

Firstly. Apologies for the goof with the undefined fileList variable. The initialisation of this variable is there in my test script (which I still have on my desktop), but somehow didn;t get into what I posted.

Your problem with foldertemp is due to the fact that you’ve initialised it outside the handler, but are trying to use it inside the handler. The variable inside the handler is local and won’t be the same as the other unless you declare them as global. But you don’t want a global here. Just initialise foldertemp inside the handler instead. (It would, of course, get initialised anyway if the loop happened to process a folder before any files, but you can’t rely on this happening - as I see Brandon has pointed out.)

global fileList

set fileList to ""

set carriage_return to return as text
set message_A to "This script will generate a list of all folder-filename.ext pairs into a text file called Folder Listing.txt" & carriage_return & carriage_return & "Select the folder to catalog (and let the script run)."
display dialog message_A buttons {"OK"} default button 1 with icon note

on listFolder(f, s)
  set foldertemp to "" -- initialise foldertemp here

  list folder alias f
  repeat with i in the result
    set fp to (f & i) as alias as string
    considering case
      if fp ends with ":" then
        set foldertemp to fileList & s & i
      else
        set fileList to foldertemp & ":" & fileList & s & i & return
      end if
    end considering
  end repeat
end listFolder

-- etc.

Oops. No. That’s useless too. The goalposts have apparently moved since last time. It was an indented list. Now it’s something else. (Full path strings?) Please explain exactly how you want the output to appear.

Reading back over the thread again, perhaps you mean something like this. I’ve added some comments to help you tailor it if it’s close.

global fileList

on listFolder(f)
  list folder f -- or, if preferred: list folder f without invisibles
  repeat with i in the result
    set fp to (f & i) as alias as string
    considering case
      if fp ends with ":" then
        listFolder(fp)
      else
        set fileList to fileList & fp & return
      end if
    end considering
  end repeat
end listFolder

set fileList to ""
set message_A to "This script will generate a list of all folder-filename.ext pairs into a text file called Folder Listing.txt" & return & return & "Select the folder to catalog (and let the script run)."
display dialog message_A buttons {"OK"} default button 1 with icon note
set theFolder to (choose folder "Select a folder to list :") as string

-- Make a text containin the full path strings of every file in the folder and its subfolders
listFolder(theFolder)

set astid to AppleScript's text item delimiters

-- Edit out the first part of each path, up to and including the folder name
set AppleScript's text item delimiters to theFolder
set textItems to fileList's text items
set AppleScript's text item delimiters to ""
set fileList to textItems as string

-- Replace the remaining colons with tabs
set AppleScript's text item delimiters to ":"
set textItems to fileList's text items
set AppleScript's text item delimiters to tab -- or, perhaps, to " (f)" & tab
set fileList to textItems as string

set AppleScript's text item delimiters to astid

set listFile to (theFolder & "Folder Listing.txt")

set fRef to (open for access file listFile with write permission)
try
  set eof fRef to 0
  write fileList to fRef
end try
close access fRef
set fileList to ""

NG (and everyone else),

This last script functioned perfectly. At first I didn’t think it would, thanks to that annoying habit of 10.2.8 which doesn’t immediately update the file list in the Finder. I was about to post that the script didn’t work, but did a search on the hard drive for ‘folder listing’ and sure enough, there she sat.

Now I have to back-engineer it to work in OS 9.1, as we’ll have to run it on older Macs, too.

Wonderful tool, this AppleScript. Frustrating, but wonderful. Thanks for the help.

Ya know – I just never expected this to work in OS 9/Classic. But it does. Wow.

Now to try it on a native 9.1 platform. You guys rock ! thanks for the help

OK – so it works in OS 9 natively. Sort of. The Applescript Editor (1.8.3) has a default RAM allocation of 1500k. Obviously, I can boost what this thing can index by upping that RAM allocation, but would it be smarter to have the script write to a file while it is collecting the name/path information? If it were to write every 50 or 100 lines collected, would this be sufficient to free up enough RAM to all the script to function indefinitely? Or does that RAM get released in Applescript? In what other ways can we avoid crashing Applescript and processing potentially large lists?

That seems to be right. I’ve now revamped the script to try and make it more memory-efficient in its text handling and to write to the listing file in batches of 500 lines. (I get an “out of memory” error with batches of 1000 lines, so the optimum is probably somewhere in between.) This has just successfully listed every file (including invisibles) on the hard disk of my OS 8.6 system:

global fileList, subpOffset, fRef, fileCount

on listFolder(f)
	list folder f -- or, if preferred: list folder f without invisibles
	repeat with i in the result
		set fp to (f & i) as alias as string
		considering case
			if fp ends with ":" then
				listFolder(fp)
			else
				-- Remove the path to the root folder from the path to this item
				set subp to text subpOffset thru -1 of fp
				-- Replace any remaining colons with tabs
				if subp contains ":" then
					set AppleScript's text item delimiters to ":"
					set textItems to subp's text items
					set AppleScript's text item delimiters to tab -- or, perhaps, to " (f)" & tab
					set subp to textItems as string
				end if
				-- Append the doctored line to fileList
				set the end of my fileList to subp
				set fileCount to fileCount + 1
				if fileCount > 499 then writeTextToFile()
			end if
		end considering
	end repeat
end listFolder

on writeTextToFile()
	-- Write the list as text to the file with delimiter return
	set AppleScript's text item delimiters to return
	write (fileList as string) to fRef
	write return to fRef
	-- Reinitialise the list
	set fileList to {}
	set fileCount to 0
end writeTextToFile


set message_A to "This script will generate a list of all folder-filename.ext pairs into a text file called Folder Listing.txt" & return & return & "Select the folder to catalog (and let the script run)."
display dialog message_A buttons {"OK"} default button 1 with icon note

-- The path string of the folder to be listed
set theFolder to (choose folder "Select a folder to list :") as string
-- The offset of the name of each item of the folder in its own path string
set subpOffset to (count theFolder) + 1
-- A list for collecting doctored path strings for every file in the folder and its subfolders.
-- "Doctored" means "not including the path to the folder itself and with tabs instead of colons."
set fileList to {}
-- The number of items currently in the list (saves counting every time)
set fileCount to 0

set ASTID to AppleScript's text item delimiters

set listFile to theFolder & "Folder Listing.txt"
set fRef to (open for access file listFile with write permission)
try
	set eof fRef to 0
	-- Assemble a list of doctored path strings, writing to file along the way if necessary
	listFolder(theFolder)
	-- Write any remaining path strings to the file
	if fileCount > 0 then writeTextToFile()
on error msg
	display dialog msg buttons {"OK"} default button 1 with icon stop
end try
close access fRef

set AppleScript's text item delimiters to ASTID

-- Zap the list from this script and update the folder
set fileList to ""
repeat 2 times
	tell application "Finder"
		(*
    -- The file may be too long for SimpleText!
    set creator type of file listFile to "R*ch" -- BBEdit
    *)
		update folder theFolder
	end tell
end repeat

with timeout of (24 * hours) seconds
	display dialog "Folder listing completed." buttons {"OK"} default button 1 with icon note
end timeout