I'm working on a function that flattens a directory by moving the contents of all subfolders into the current folder, operating recursively as needed. The last detail is to delete empty folders after taking all the stuff out but the DeleteFolder function lacks a flag to prevent user-prompts. I don't want to change Misc->"Operations that overwrite/delete..." because this function is intended to share and other users would not appreciate having to change this option. (It is a good one!)
Hopefully there is a way to programmatically (and temporarily) change that setting. Or there is some secret flag for DeleteFolder. Or someone knows how to use MoveFolder to relocate empty folders to the Recycle Bin/Trash. Or relocate them to a temp dir so the OS can delete them? None of these ideas seem plausible... Does anyone have suggestions?
There is no programmatic way to temporarily set the Misc Settings setting about deleting folders on disk. There is also no programmatic way to know what that setting is so you could tell the user to change it and rerun the procedure.
You may be able to use ExecuteScriptText to move the folder to the trash. That would have to be done differently for Macintosh and Windows.
You could move the folder to the Temporary directory. Use SpecialDirPath to get the users Temporary directory. Looking at my temporary directory on Mac OS X, it appears that the OS does empty it when you shut down or restart. On Windows XP, it appears that the OS does not empty it. That may not be a big deal.
You could create a folder and move all of the empty folders into it for neatness, and tell the user to manually delete it or move it into the temporary folder.
I'm working on a function that flattens a directory by moving the contents of all subfolders into the current folder, ... Does anyone have suggestions?
I would suggest, rather than doing such directory-level management from in Igor, to ship out a command to let the OS handle it. IOW, create a shell script / AppleScript on Mac or whatever is equivalent on Windows. Have the script take the directory name and go with it. Have it return OK / NOT OK flags. Then, run the script from Igor (ExecuteScript ...) either as a background task or a pre-emptive task that waits for proper return before continuing.
I would also suggest having the shell script first create a copy of the directory (perhaps even an archive) in case a mistake is made and a restore option is needed.
--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
Here is a function I use to do this. I don't recall why, on Windows, I'm writing a temporary batch file and executing that file instead of just executing the rmdir command directly. It is probably that I already had a function to execute a command via a batch script and collect the output of the batch script so that it can be read by Igor, and I took that function and modified it as little as necessary to get what I wanted.
//** // Forcefully delete a folder. DeleteFolder also // works but if the user hasn't told Igor not to prompt // them before doing so then the tests won't finish running. // // BE CAREFUL USING THIS. //* staticFunction ForceDeleteFolder(fullPathToFolder) String fullPathToFolder Variable error String platform = IgorInfo(2) String errorMessage String envInfoString StrSwitch(platform) Case"Windows": try // Do some setup. String tmpDir = SpecialDirPath("Temporary", 0, 0, 0)
AbortOnValue (numtype(strlen(tmpDir))!= 0||!(strlen(tmpDir)>0)), 1
// Make sure that the directory we just got is, in fact, a directory. GetFileFolderInfo/Q tmpDir
AbortOnValue (V_Flag >= 0&&!V_isFolder), 3
// Set an Igor symbolic path to the temporary directory. String tmpDirectoryPath = UniqueName("tmpPath", 12, 0) NewPath/Q $(tmpDirectoryPath), tmpDir
AbortOnValue (V_flag), 5// Setting of the new path failed.
// Write a temporary batch file to the temporary directory that will // call the "rmdir" command from the command shell and save the // output of the command to a temporary file. String tempBatFileName, tempResultsFileName String tempBatFileFullPath, tempResultsFileFullPath Variable tempBatFileRefNum, tempResultsFileRefNum
// Add a path separator character to the end of the path, if necessary, and add on the file name.
tempBatFileFullPath = ParseFilePath(2, tmpDir, ":", 0, 0) + tempBatFileName
// Convert the path into a windows path that uses "\" as the path separator.
tempBatFileFullPath = ParseFilePath(5, tempBatFileFullPath, "\\", 0, 0)
// /s deletes all contents of directory and then the directory // /q prevents DOS from asking us if we're sure. String windowsFilePath = ParseFilePath(5, fullPathToFolder, "\\", 0, 0) if(strlen(windowsFilePath)<= 0)
AbortOnValue 1, 8 endif fprintf tempBatFileRefNum, "rmdir \"%s\"/s /q \r\n", windowsFilePath Close tempBatFileRefNum
// Call the batch file we just created. Timeout after 2 seconds if this doesn't succeed. String scriptText sprintf scriptText, "cmd.exe /C \"%s\"", tempBatFileFullPath ExecuteScriptText/W=2/Z scriptText
// Check for an error.
AbortOnValue (V_flag != 0), 9
// Delete the temporary batch file and temporary results file. DeleteFile/P=$(tmpDirectoryPath)/Z=1 tempBatFileName // If we get an error here we don't really care. We've already got // the goods, so just run. break catch
error = -1 endtry
Case"Macintosh": // This is a lot easier on the Macintosh because we can just use AppleScript. try // Figure out the POSIX path of the folder to delete. String posixPath = ParseFilePath(5, fullPathToFolder, "/", 0, 0) if(strlen(posixPath)>0) String appleScriptCommand sprintf appleScriptCommand, "tell application \"Finder\" to delete ((POSIX file \"%s\") as alias)", posixPath ExecuteScriptText/Z appleScriptCommand
AbortOnValue V_flag, 1 endif
envInfoString = S_Value catch
errorMessage = "Could not execute AppleScript command." print errorMessage
error = -1 endtry break EndSwitch if(numtype(strlen(tmpDirectoryPath)) == 0)// Check for a null string KillPath/Z $(tmpDirectoryPath) endif return error End
Thanks for the excellent ideas everyone. This is getting tricky and it's compounded by my decision to make it as Igor-driven as possible. I already have a batch script which flattens directories but it requires a user to either place it in the folder or know how operate the command line. Plus it's a Windows batch file... sorry Mac users. Writing it in Igor makes it more portable, transparent and flexible.
hrodstein, you read my mind about checking the setting and prompting the user - that option dawned on me last night. And good tip on the Mac temporary folder being emptied upon restart. That may become part of the solution.
Since I think users would prefer to have their files recoverable, I'm going to try to move the folders to the recycle bin/trash rather than delete them outright. On Windows, finding the user's recycle bin path is not trivial so rather than MoveFolder, I'm going to try ExecuteScriptText with a third-party program called recycle.exe, part of CmdUtils from MaDdoG. On Mac, I'll code it to move files into the tempdir but I wonder if the AppleScript from aclight could be made to recycle instead of delete. (I don't have a Mac... maybe it already does)
There is no programmatic way to temporarily set the Misc Settings setting about deleting folders on disk. There is also no programmatic way to know what that setting is so you could tell the user to change it and rerun the procedure.
You may be able to use ExecuteScriptText to move the folder to the trash. That would have to be done differently for Macintosh and Windows.
You could move the folder to the Temporary directory. Use SpecialDirPath to get the users Temporary directory. Looking at my temporary directory on Mac OS X, it appears that the OS does empty it when you shut down or restart. On Windows XP, it appears that the OS does not empty it. That may not be a big deal.
You could create a folder and move all of the empty folders into it for neatness, and tell the user to manually delete it or move it into the temporary folder.
October 12, 2011 at 08:08 pm - Permalink
I would suggest, rather than doing such directory-level management from in Igor, to ship out a command to let the OS handle it. IOW, create a shell script / AppleScript on Mac or whatever is equivalent on Windows. Have the script take the directory name and go with it. Have it return OK / NOT OK flags. Then, run the script from Igor (ExecuteScript ...) either as a background task or a pre-emptive task that waits for proper return before continuing.
I would also suggest having the shell script first create a copy of the directory (perhaps even an archive) in case a mistake is made and a restore option is needed.
--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
October 13, 2011 at 07:59 am - Permalink
// Forcefully delete a folder. DeleteFolder also
// works but if the user hasn't told Igor not to prompt
// them before doing so then the tests won't finish running.
//
// BE CAREFUL USING THIS.
//*
static Function ForceDeleteFolder(fullPathToFolder)
String fullPathToFolder
Variable error
String platform = IgorInfo(2)
String errorMessage
String envInfoString
StrSwitch (platform)
Case "Windows":
try
// Do some setup.
String tmpDir = SpecialDirPath("Temporary", 0, 0, 0)
AbortOnValue (numtype(strlen(tmpDir)) != 0 || !(strlen(tmpDir) > 0)), 1
// Make sure that the directory we just got is, in fact, a directory.
GetFileFolderInfo/Q tmpDir
AbortOnValue (V_Flag >= 0 && !V_isFolder), 3
// Set an Igor symbolic path to the temporary directory.
String tmpDirectoryPath = UniqueName("tmpPath", 12, 0)
NewPath/Q $(tmpDirectoryPath), tmpDir
AbortOnValue (V_flag), 5 // Setting of the new path failed.
// Write a temporary batch file to the temporary directory that will
// call the "rmdir" command from the command shell and save the
// output of the command to a temporary file.
String tempBatFileName, tempResultsFileName
String tempBatFileFullPath, tempResultsFileFullPath
Variable tempBatFileRefNum, tempResultsFileRefNum
sprintf tempBatFileName, "IgorCommandScript_%.4u.bat", abs(trunc(StopMsTimer(-2)))
Open/P=$(tmpDirectoryPath)/T=".bat"/Z=1 tempBatFileRefNum as tempBatFileName
AbortOnValue (V_flag != 0), 7
// Add a path separator character to the end of the path, if necessary, and add on the file name.
tempBatFileFullPath = ParseFilePath(2, tmpDir, ":", 0, 0) + tempBatFileName
// Convert the path into a windows path that uses "\" as the path separator.
tempBatFileFullPath = ParseFilePath(5, tempBatFileFullPath, "\\", 0, 0)
// /s deletes all contents of directory and then the directory
// /q prevents DOS from asking us if we're sure.
String windowsFilePath = ParseFilePath(5, fullPathToFolder, "\\", 0, 0)
if (strlen(windowsFilePath) <= 0)
AbortOnValue 1, 8
endif
fprintf tempBatFileRefNum, "rmdir \"%s\"/s /q \r\n", windowsFilePath
Close tempBatFileRefNum
// Call the batch file we just created. Timeout after 2 seconds if this doesn't succeed.
String scriptText
sprintf scriptText, "cmd.exe /C \"%s\"", tempBatFileFullPath
ExecuteScriptText/W=2/Z scriptText
// Check for an error.
AbortOnValue (V_flag != 0), 9
// Delete the temporary batch file and temporary results file.
DeleteFile/P=$(tmpDirectoryPath)/Z=1 tempBatFileName
// If we get an error here we don't really care. We've already got
// the goods, so just run.
break
catch
error = -1
endtry
Case "Macintosh":
// This is a lot easier on the Macintosh because we can just use AppleScript.
try
// Figure out the POSIX path of the folder to delete.
String posixPath = ParseFilePath(5, fullPathToFolder, "/", 0, 0)
if (strlen(posixPath) > 0)
String appleScriptCommand
sprintf appleScriptCommand, "tell application \"Finder\" to delete ((POSIX file \"%s\") as alias)", posixPath
ExecuteScriptText/Z appleScriptCommand
AbortOnValue V_flag, 1
endif
envInfoString = S_Value
catch
errorMessage = "Could not execute AppleScript command."
print errorMessage
error = -1
endtry
break
EndSwitch
if (numtype(strlen(tmpDirectoryPath)) == 0) // Check for a null string
KillPath/Z $(tmpDirectoryPath)
endif
return error
End
October 13, 2011 at 08:35 am - Permalink
hrodstein, you read my mind about checking the setting and prompting the user - that option dawned on me last night. And good tip on the Mac temporary folder being emptied upon restart. That may become part of the solution.
Since I think users would prefer to have their files recoverable, I'm going to try to move the folders to the recycle bin/trash rather than delete them outright. On Windows, finding the user's recycle bin path is not trivial so rather than MoveFolder, I'm going to try ExecuteScriptText with a third-party program called recycle.exe, part of CmdUtils from MaDdoG. On Mac, I'll code it to move files into the tempdir but I wonder if the AppleScript from aclight could be made to recycle instead of delete. (I don't have a Mac... maybe it already does)
October 13, 2011 at 11:14 am - Permalink
See http://macscripter.net/viewtopic.php?id=24394
October 13, 2011 at 02:04 pm - Permalink
See http://macscripter.net/viewtopic.php?id=24394[/quote]
I found the same page. Thanks aclight! I should just refresh the forums more often. :)
October 13, 2011 at 02:22 pm - Permalink
It behaved well on Windows. I expect it should work on Macintosh too.
October 13, 2011 at 05:52 pm - Permalink