Tuesday, May 11, 2021

#1 2021-02-10 08:21:24 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 872

Copy a file with an existing file at the target

NSFileManager's copyItemAtPath method does not overwrite an existing file and instead returns an error. So, to overwrite an existing file, it seems reasonable to first employ removeItemAtPath, and that's the approach employed in the script below.

This all seems reasonable until the copy fails for some reason. Then, the script has deleted the existing file and can't copy the new file. I can only think of two alternatives. The first is to rename the existing file and then to rename it back if the copy is not successful. The second is to move the existing file to NSTemporaryDirectory and then to move it back if the copy fails. Is there a better way to handle this? Thanks.

Applescript:

use framework "Foundation"
use scripting additions

set theFile to POSIX path of (choose file)
set theFolder to POSIX path of (choose folder)
copyFile(theFile, theFolder)

on copyFile(POSIXPath, folderPath) -- from Shane's book with modification
   set POSIXPath to current application's NSString's stringWithString:POSIXPath
   set folderPOSIXPath to current application's NSString's stringWithString:folderPath
   set theName to POSIXPath's lastPathComponent()
   set newPath to folderPOSIXPath's stringByAppendingPathComponent:theName
   set fileManager to current application's NSFileManager's defaultManager()
   
   -- remove existing file - not in Shane's script
   if (fileManager's fileExistsAtPath:newPath) then (fileManager's removeItemAtPath:newPath |error|:(missing value))
   
   set {theResult, theError} to fileManager's copyItemAtPath:POSIXPath toPath:newPath |error|:(reference)
   if (theResult as boolean) is false then
       error (theError's localizedDescription() as text)
   end if
end copyFile

Last edited by peavine (2021-02-10 08:41:49 am)


2018 Mac mini - macOS Catalina

Offline

 

#2 2021-02-10 10:53:05 am

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 118

Re: Copy a file with an existing file at the target

It looks like the "replaceItemAtURL:withItemURL:backupItemName:resultingItemURL:error:" NSFileManager function might be the better way to do it, although I've not tried it myself, but a previous forum posting here shows an example of how to use it.

https://macscripter.net/viewtopic.php?pid=204924

And it seems to me, that backing up the file in a temporary directory first would be the wise move.

Regards Mark

Last edited by Mark FX (2021-02-10 10:54:27 am)

Offline

 

#3 2021-02-10 03:38:03 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 872

Re: Copy a file with an existing file at the target

Thanks Mark for the response. I tested that method and it works just as I want but it does a move rather than a copy. I guess I could copy the file back from the destination to the source but that doesn't seem very good. I'll have to give this some more thought.

My test script:

Applescript:

use framework "Foundation"
use scripting additions

set sourceFile to POSIX path of (path to desktop) & "Test File.txt"
set destinationFile to POSIX path of (path to documents folder) & "Test File.txt"

copyAndReplace(sourceFile, destinationFile)

on copyAndReplace(sourceFile, destinationFile)
   set sourceURL to current application's |NSURL|'s fileURLWithPath:sourceFile
   set destinationURL to current application's |NSURL|'s fileURLWithPath:destinationFile
   set filemanager to current application's NSFileManager's defaultManager
   set {theResult, theError} to filemanager's replaceItemAtURL:destinationURL withItemAtURL:sourceURL backupItemName:(missing value) options:0 resultingItemURL:(missing value) |error|:(reference)
end copyAndReplace

Last edited by peavine (2021-02-10 03:40:42 pm)


2018 Mac mini - macOS Catalina

Offline

 

#4 2021-02-10 04:36:24 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: Copy a file with an existing file at the target

The suggested safe approach is to create a temporary directory, copy to that, use replaceItemAtURL::::: with the copied version, then delete the temp directory. You use URLForDirectory:inDomain:appropriateForURL:create:error: to create the (NSItemReplacementDirectory) directory, which will usually be in the same directory as the target.

This is the handler from my FileManagerLib library:

Applescript:

-- This handler is called by other handlers
on atomicReplaceFromURL:sourceURL toURL:destinationURL
   set theFileManager to NSFileManager's |defaultManager|()
   set {tempURLDir, theError} to theFileManager's URLForDirectory:99 inDomain:1 appropriateForURL:destinationURL create:true |error|:(reference) -- 1 = NSUserDomainMask, 99 = NSItemReplacementDirectory
   if tempURLDir is missing value then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
   set tempDestURL to tempURLDir's URLByAppendingPathComponent:(destinationURL's lastPathComponent())
   set {theResult, theError} to (theFileManager's copyItemAtURL:sourceURL toURL:tempDestURL |error|:(reference))
   if not theResult as boolean then
       theFileManager's removeItemAtURL:tempURLDir |error|:(missing value)
       error (theError's |localizedDescription|() as text) number (theError's code() as integer)
   end if
   set {theResult, theError} to theFileManager's replaceItemAtURL:destinationURL withItemAtURL:tempDestURL backupItemName:(missing value) options:1 resultingItemURL:(missing value) |error|:(reference) -- 1 = NSFileManagerItemReplacementUsingNewMetadataOnly
   theFileManager's removeItemAtURL:tempURLDir |error|:(missing value)
   -- if replacement failed, return error
   if not theResult as boolean then error (theError's |localizedDescription|() as text) number (theError's code() as integer)
end atomicReplaceFromURL:toURL:


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#5 2021-02-10 06:25:33 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 872

Re: Copy a file with an existing file at the target

Thanks Shane. Your description of the safe approach and of how replaceItemAtURL is utilized in this approach is excellent and makes perfect sense.


2018 Mac mini - macOS Catalina

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)