Hello.
I knew Nigel Garvey had made a very optimized algorithm for this job, I adapted this particular task to that algorithm.
I have tested it, and it works, but I haven’t timed it.
From the timings I read in the post, this should work considerably faster no matter the number of folders, but it should excel proportionally with the number of folders..
Here it is, thanks to Nigel Garvey from the thread Suggestions For Duplicating A Folder Structure
” © Nigel Garvey 2007
considering case
main()
end considering
on main()
” T I N K E R E D B Y McUsr to fit with the job description, which is to replicate a folder structure onto a new
” disk image. original post: macscripter.net/viewtopic.php?pid=75224#p75224
set fromDisk to false
if fromDisk is true then
set sourceFolder to (choose from list (list disks))
if sourceFolder is false then return
set rootPath to POSIX path of alias (item 1 of sourceFolder)
else
set rootPath to POSIX path of (choose folder)
end if
-- Get the POSIX paths of the hierarchy's subfolders. (The double slashes in the shell script result will come out in the wash.)
set subfolderPaths to rest of paragraphs of (do shell script "find " & quoted form of rootPath & " -name \"*\" -type d" with administrator privileges)
if (subfolderPaths is {}) then set subfolderPaths to {""} -- No subfolders in the hierarchy.
-- Construct a POSIX path (without a trailing slash) for the root folder of the duplicate hierarchy,
-- ensuring that the folder name's unique at the destination.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/" as Unicode text
set rootName to text item -2 of rootPath
set newRootName to rootName
set destinationFolder to (path to desktop as Unicode text)
set namesAtDestination to (list folder destinationFolder)
set n to 0
repeat while (newRootName is in namesAtDestination)
set n to n + 1
set newRootName to rootName & " " & n
end repeat
set newRootPath to (POSIX path of destinationFolder) & newRootName
set dmgPath to quoted form of (newRootPath & ".dmg")
set AppleScript's text item delimiters to astid
-- Construct and use a single mkdir command for the entire duplicate hierarchy.
do shell script getMkdirString(subfolderPaths, newRootPath)
do shell script "/usr/bin/hdiutil create -srcfolder " & quoted form of newRootPath & " -megabytes 100 " & dmgPath
do shell script "/bin/rm -rf " & quoted form of newRootPath
end main
on getMkdirString(subfolderPaths, newRootPath)
script o
property paths : subfolderPaths
property outputComponents : {"mkdir -p " & quoted form of newRootPath}
-- Some pre-defined stuff for speed.
property slash : "/" as Unicode text
property slashBrace : "/{" as Unicode text
property comma : "," as Unicode text
property braceComma : "}," as Unicode text
-- A recursive subhandler that analyses how the paths relate to each other
-- and decides how to structure the 'mkdir' string.
on parsePaths(a, b)
-- At each recursion, items a thru b of the list represent sibling subfolders and their descendents.
-- Each sibling is sorted immediately before its own descendents, so item a must be an immediate
-- child of the previous level. The other items are either its decendents, siblings, or siblings' descendents.
set currentSibling to item a of my paths
if (a = b) then
-- Item a is a childless subfolder with no siblings. Append "/'<name>'," to the output.
set end of my outputComponents to slash & quoted form of text item -1 of currentSibling & comma
else
if (item b of my paths begins with currentSibling) then
-- Item a has descendents but no siblings. Append "/'<name>'" to the output.
set end of my outputComponents to slash & quoted form of text item -1 of currentSibling
set closingBraceRequired to false
else
-- Item a has siblings. It may or may not have descendents. Append "/{'<name>'" to the output.
set end of my outputComponents to slashBrace & quoted form of text item -1 of currentSibling
set closingBraceRequired to true
end if
-- Check the other paths in this recursion.
set i to a + 1
repeat with j from i to b
if (item j of my paths begins with currentSibling) then
-- This is a descendent of the current sibling. Skip it for now.
else
-- This is a new sibling.
if (j > i) then
-- If the previous sibling had descendents, recurse to sort them out.
parsePaths(i, j - 1)
else
-- Otherwise, just append a comma to the output.
set end of my outputComponents to comma
end if
-- Append the new sibling's name to the output and continue the repeat.
set currentSibling to item j of my paths
set end of my outputComponents to quoted form of text item -1 of currentSibling
set i to j + 1
end if
end repeat
-- At the end of the repeat, sort out any descendents of the last sibling.
if (i > b) then
else
parsePaths(i, b)
end if
-- Append a closing brace and a comma to the output if there were siblings at this level.
if (closingBraceRequired) then set end of my outputComponents to braceComma
end if
end parsePaths
end script
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
tell o to parsePaths(1, (count subfolderPaths))
-- Coerce the resulting list to Unicode text and tidy up the result.
set AppleScript's text item delimiters to ""
set mkdirString to o's outputComponents as Unicode text
set mkdirString to text 1 thru -2 of mkdirString
set AppleScript's text item delimiters to ",}"
set mkdirString to mkdirString's text items
set AppleScript's text item delimiters to "}"
set mkdirString to mkdirString as Unicode text
set AppleScript's text item delimiters to astid
return mkdirString
end getMkdirString