Silent folder deletion

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?

This is indeed a tricky problem.

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.



pokeeffe wrote:
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.
//*
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
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)

[quote=aclight]I haven't tested it, but some Googling suggests that you could use the following line to recycle on Macintosh:

sprintf appleScriptCommand, "tell application \"Finder\"  to move ((POSIX file \"%s\") as alias) to trash", posixPath


See http://macscripter.net/viewtopic.php?id=24394[/quote]

I found the same page. Thanks aclight! I should just refresh the forums more often. :)