Recursive Folder Copy Script
If you need to copy a folder with files recursively to another location, you may just call our Files.CopyFile function. Beside the function having file in the name, it does copy folder hierarchies. But sometimes you need to do something special, like only copy some files (e.g. hidden ones), skip over errors or not copy all folders. For this case we can write a script to do the copy process recursively and allow you to customize it.
First we show you the calling script, which passes source and destination path as part of a JSON object:
# CopyFolder in file FileOperation
Set Variable [ $SourcePath ; Value: FileOperation::SourcePath ]
Set Variable [ $DestPath ; Value: FileOperation::DestinationPath ]
#
Set Variable [ $json ; Value: "{}" ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "SourcePath"; $SourcePath ) ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "DestPath"; $DestPath ) ]
#
Perform Script [ Specified: From list ; “CopyFolder Recursive” ; Parameter: $json ]
Set Variable [ $$result ; Value: Get(ScriptResult) ]
#
Show Custom Dialog [ "done" ; $$result ]
If the copy is done, we just show a message dialog. But it may be good to look if Error is set in the JSON to know whether it worked or not. Next we look on the more complex recursive script:
# CopyFolder Recursive in file FileOperation
Set Variable [ $json ; Value: Get(ScriptParameter) ]
#
# query the input and output paths we got
Set Variable [ $SourcePath ; Value: MBS( "JSON.GetPathItem"; $json; "SourcePath"; 3) ]
Set Variable [ $DestPath ; Value: MBS( "JSON.GetPathItem"; $json; "DestPath"; 3) ]
Set Variable [ $Recursion ; Value: MBS( "JSON.GetPathItem"; $json; "Recursion"; 3) ]
If [ IsEmpty($Recursion) ]
# called by user, so check input
#
# check source
Set Variable [ $r ; Value: MBS( "Files.DirectoryExists"; $SourcePath ) ]
If [ $r ≠ 1 ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "Error"; "Invalid source path") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "ErrorDetail"; $r) ]
Exit Script [ Text Result: $json ]
End If
#
# check dest. Create as folder. If already exists, this will not be an error
Set Variable [ $r ; Value: MBS( "Files.CreateDirectory"; $DestPath ) ]
If [ MBS("IsError") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "Error"; "Failed to create directory: " & $DestPath) ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "ErrorDetail"; $r) ]
Exit Script [ Text Result: $json ]
End If
#
Set Variable [ $r ; Value: MBS( "Files.DirectoryExists"; $DestPath) ]
If [ $r ≠ 1 ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "Error"; "Invalid destination path") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "ErrorDetail"; $r) ]
Exit Script [ Text Result: $json ]
End If
End If
#
# get directory listing
Set Variable [ $list ; Value: MBS("Files.List"; $SourcePath) ]
If [ MBS("IsError") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "Error"; "Failed to get directory listing") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "ErrorDetail"; $list) ]
Exit Script [ Text Result: $json ]
End If
#
# loop counting up from 1 to $count
Set Variable [ $count ; Value: ValueCount ( $list ) ]
Set Variable [ $index ; Value: 1 ]
If [ $index ≤ $count ]
Loop
# your script steps here
Set Variable [ $ItemName ; Value: GetValue($list; $index) ]
Set Variable [ $ItemPath ; Value: MBS( "Path.AddPathComponent"; $SourcePath; $ItemName ) ]
Set Variable [ $ItemDest ; Value: MBS( "Path.AddPathComponent"; $DestPath; $ItemName ) ]
#
If [ MBS( "Files.DirectoryExists"; $ItemPath ) ]
# copy folder with recursion
#
# create new folder
Set Variable [ $r ; Value: MBS( "Files.CreateDirectory"; $ItemDest ) ]
#
If [ MBS("IsError") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "Error"; "Failed to create directory: " & $ItemDest) ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "ErrorDetail"; $r) ]
Exit Script [ Text Result: $json ]
End If
#
# prepare JSON for recursive call
Set Variable [ $jsonCall ; Value: "{}" ]
Set Variable [ $jsonCall ; Value: MBS( "JSON.SetPathItem"; $jsonCall; "SourcePath"; MBS( "JSON.CreateString"; $ItemPath)) ]
Set Variable [ $jsonCall ; Value: MBS( "JSON.SetPathItem"; $jsonCall; "DestPath"; MBS( "JSON.CreateString"; $ItemDest)) ]
Set Variable [ $jsonCall ; Value: MBS( "JSON.SetPathItem"; $jsonCall; "Recursion"; "true") ]
#
Perform Script [ Specified: From list ; “CopyFolder Recursive” ; Parameter: $jsonCall ]
Set Variable [ $jsonResult ; Value: Get(ScriptResult) ]
#
# check error
Set Variable [ $Error ; Value: MBS( "JSON.GetPathItem"; $jsonResult; "Error"; 3 ) ]
If [ Length ( $Error ) > 0 ]
# pass back
Exit Script [ Text Result: $jsonResult ]
End If
#
# handle statistics
Set Variable [ $FilesCopied1 ; Value: MBS( "JSON.GetPathItem"; $json; "FilesCopied"; 3 ) ]
Set Variable [ $FilesCopied2 ; Value: MBS( "JSON.GetPathItem"; $jsonResult; "FilesCopied"; 3 ) ]
Set Variable [ $json ; Value: MBS( "JSON.SetPathItem"; $json; "FilesCopied"; ( 0 + $FilesCopied1 + $FilesCopied2)) ]
#
Set Variable [ $DirectoriesCopied1 ; Value: MBS( "JSON.GetPathItem"; $json; "DirectoriesCopied"; 3 ) ]
Set Variable [ $DirectoriesCopied2 ; Value: MBS( "JSON.GetPathItem"; $jsonResult; "DirectoriesCopied"; 3 ) ]
Set Variable [ $json ; Value: MBS( "JSON.SetPathItem"; $json; "DirectoriesCopied"; $DirectoriesCopied1 + $DirectoriesCopied2 + 1) ]
#
Set Variable [ $BytesCopied1 ; Value: MBS( "JSON.GetPathItem"; $json; "BytesCopied"; 3 ) ]
Set Variable [ $BytesCopied2 ; Value: MBS( "JSON.GetPathItem"; $jsonResult; "BytesCopied"; 3 ) ]
Set Variable [ $json ; Value: MBS( "JSON.SetPathItem"; $json; "BytesCopied"; 0 + $BytesCopied1 + $BytesCopied2) ]
#
Else
# copy one file
Set Variable [ $r ; Value: MBS( "Files.ReadAndWriteFile"; $ItemPath; $ItemDest ) ]
#
If [ MBS("IsError") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "Error"; "Failed to copy file") ]
Set Variable [ $json ; Value: MBS( "JSON.AddStringToObject"; $json; "ErrorDetail"; $r) ]
Exit Script [ Text Result: $json ]
End If
#
# handle statistics
Set Variable [ $FileSize ; Value: MBS( "Files.FileSize"; $ItemPath ) ]
Set Variable [ $FilesCopied ; Value: MBS( "JSON.GetPathItem"; $json; "FilesCopied"; 3 ) ]
Set Variable [ $json ; Value: MBS( "JSON.SetPathItem"; $json; "FilesCopied"; $FilesCopied + 1) ]
Set Variable [ $BytesCopied ; Value: MBS( "JSON.GetPathItem"; $json; "BytesCopied"; 3 ) ]
Set Variable [ $json ; Value: MBS( "JSON.SetPathItem"; $json; "BytesCopied"; 0 + $BytesCopied + $FileSize) ]
End If
#
# next
Set Variable [ $index ; Value: $index + 1 ]
Exit Loop If [ $index > $count ]
End Loop
End If
#
Exit Script [ Text Result: $json ]
This script got long for an example, but it includes error handling already. We pass in the request as JSON and return JSON with status and error details. First IF block checks incoming parameter if call is not a recursion. This way the parameter check only happens first time and we can verify the source and destination paths are valid. Please note that Files.CreateDirectory does not cause an error if the folder exists already. So we can just call it to create folder if needed.
The next block is to query the directory listing with Files.List function. Here you could do an additional parameter to filter for not including hidden items or to only find files of a certain type.
Next follows the big block with the loop. We go over the list of the file names and use our Path.AddPathComponent function to create the new source and destination paths. If the source item is a folder, we do the recursion. For that we build a new json object with the new paths and call the same script again. If the called script returns an error, we would pass it up the call chain. Otherwise we update the statistics for how much we copied by merging the result with our JSON.
To copy a file, we go here with Files.ReadAndWriteFile, but could also just use Files.CopyFile here. The read and write function just reads data and writes it to the destination, which ignores metadata. Pick the function for your use case, which may involve copy with permissions, metadata like Finder comment, custom icon or preserving date stamps. If copy succeeds, we update this statistics.
Finally the loop continues with the loop until all times are copied and we can exit the script by returning the JSON. Please let us know what you think about this and whether you spot an error, so we can improve the example script. These scripts will be included with future plugins in the FileOperation.fmp12 file.