Hi I have to create a script that searches for multiple folders all with the same name,
on a volume and then copy another folder and its contents to the searched folders.
‘04_Design’ (folder name being searched for)
‘Assets’ (folder to be copied) to go into every 04_Design folder found.
Im struggling… i have found lots of folder finding solutions but none for multiple instances.
so none of my scripts work.
This should work, so long as the volume is indexed in spotlight. I used mdfind because it’s much faster than any AppleScript equivalent.
set theFolders to paragraphs of (do shell script "/usr/bin/mdfind -onlyin " & quoted form of "/Volumes/volume name" & " 'kMDItemFSName = \"04_Design\" && kMDItemContentType = \"public.folder\"'")
repeat with thisFolder in theFolders
-- need this weird coercion because the Finder only likes HFS (colon-delimited) paths or aliases
set folderPath to (POSIX file thisFolder) as text
set assetsPath to (POSIX file "/path/to/folder/Assets") as text
tell application "Finder"
duplicate folder assetsPath to folder folderPath
end tell
end repeat
Test it on something disposable first, or make a backup of the volume; there’s no ‘undo’ option here.
I think you’ll find this is quicker (requires my Metadata Lib):
use AppleScript version "2.5" -- macOS 10.11 or later
use scripting additions
use script "Metadata Lib" version "2.0.2"
set thePath to POSIX path of (choose folder with prompt "choose the volume")
set theFolders to perform search in folders {thePath} predicate string "kMDItemFSName = %@ AND kMDItemContentType = %@" search arguments {"04_Design", "public.folder"}
-- now do duplicating
But the poster says “on a volume”, and that could well mean a volume that doesn’t support Spotlight indexing, in which case neither of our scripts are any use. In which case he could use:
use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions
-- classes, constants, and enums used
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to 4
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to 2
property NSURLIsDirectoryKey : a reference to current application's NSURLIsDirectoryKey
property NSURLIsPackageKey : a reference to current application's NSURLIsPackageKey
set thePath to POSIX path of (choose folder with prompt "choose the volume")
set volURL to current application's NSURL's fileURLWithPath:thePath
set fileManager to current application's NSFileManager's |defaultManager|()
set theOptions to (NSDirectoryEnumerationSkipsPackageDescendants + NSDirectoryEnumerationSkipsHiddenFiles)
set theKeys to {NSURLIsDirectoryKey, NSURLIsPackageKey}
set theFolders to (fileManager's enumeratorAtURL:volURL includingPropertiesForKeys:theKeys options:theOptions errorHandler:(missing value))'s allObjects()
set thePred to current application's NSPredicate's predicateWithFormat_("lastPathComponent == %@", "04_Design")
set theFolders to theFolders's filteredArrayUsingPredicate:thePred
set folderList to current application's NSMutableArray's array()
repeat with aFolder in theFolders
set {theResult, theValue} to (aFolder's getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value))
if theValue as boolean then
-- is it a package?
set {theResult, theValue} to (aFolder's getResourceValue:(reference) forKey:(NSURLIsPackageKey) |error|:(missing value))
if not theValue as boolean then -- must be folder
(folderList's addObject:aFolder)
end if
end if
end repeat
set thePaths to (folderList's valueForKey:"path") as list
-- now do the duplicating
Anything other than Spotlight-based searching is going to be slow on large volumes, though.
Using find is a little quicker than the code I posted above, depending on the volume. The problem is that find can’t distinguish between packages and directories, so it recursively searches inside packages. That not only means you risk getting results you don’t want, but if you have a lot of packages – an /Applications folder, say, – it will spend a lot of time search stuff you’re usually not interested in.
Find’s basic syntax is a shotgun approach, so it just finds everything. If you want specificity, then you have to tailor the search with the tool’s options. This should restrict searching files in packages and, optionally, named packages at the root level.
do shell script "find -E " & (path to desktop folder as text)'s POSIX path & " -name '*.*' -prune " # ! -regex '.*(scptd|app|rtfd|download|fcarch).*'
Marc. Thanks for the post. I’ve always associated packages with apps, but in fact they include much more. Perhaps that’s what Shane was referring to when he said “It’s a bit of a trap.”
use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions
-- classes, constants, and enums used
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to 4
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to 2
property NSURLIsDirectoryKey : a reference to current application's NSURLIsDirectoryKey
property NSURLIsPackageKey : a reference to current application's NSURLIsPackageKey
set folderToCopy to POSIX path of (choose folder with prompt "choose the folder to copy")
set folderToCopyURL to current application's NSURL's fileURLWithPath:folderToCopy
set folderName to folderToCopyURL's lastPathComponent()
set thePath to POSIX path of (choose folder with prompt "choose the volume")
set volURL to current application's NSURL's fileURLWithPath:thePath
set fileManager to current application's NSFileManager's |defaultManager|()
set theOptions to (NSDirectoryEnumerationSkipsPackageDescendants + NSDirectoryEnumerationSkipsHiddenFiles)
set theKeys to {NSURLIsDirectoryKey, NSURLIsPackageKey}
set theFolders to (fileManager's enumeratorAtURL:volURL includingPropertiesForKeys:theKeys options:theOptions errorHandler:(missing value))'s allObjects()
set thePred to current application's NSPredicate's predicateWithFormat_("lastPathComponent == %@", "04_Design")
set theFolders to theFolders's filteredArrayUsingPredicate:thePred
set folderList to current application's NSMutableArray's array()
repeat with aFolder in theFolders
set {theResult, theValue} to (aFolder's getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value))
if theValue as boolean then
-- is it a package?
set {theResult, theValue} to (aFolder's getResourceValue:(reference) forKey:(NSURLIsPackageKey) |error|:(missing value))
if not theValue as boolean then -- must be folder
--(folderList's addObject:aFolder)
set destURL to (aFolder's URLByAppendingPathComponent:folderName)
(fileManager's copyItemAtURL:folderToCopyURL toURL:destURL |error|:(missing value))
end if
end if
end repeat
--set thePaths to (folderList's valueForKey:"path") as list