FlattenDir: a flexible directory flattener

This function was motivated by an instrument which stores its data files in subdirectories built from the current month, day and hour. It will flatten directories by recursively lifting the contents of any subfolders into the current folder. The level of recursion can be specified by the user or it can traverse down the whole tree. Users can specify whether to overwrite files with conflicting names or not (which then leaves existing files in-place.) An optional parameter is provided to only lift files of a certain type; it defaults to all files. Another optional parameter allows the user fine-grained control over how subdirectories are handled after flattening; it defaults to deleting only empty directories. 'Deleted' files and folders should appear in the Recycle Bin on Windows or the Trash on Macintosh.

    Notes:
  • The first argument is the name of an existing path as a string
  • Windows users will need to place recycle.exe in their %windir%\system32 folder. It is available from MaDdoG software (http://www.maddogsw.com/) but is also attached to this post.
  • Macintosh users will have to test the 'move to trash' functionality on their own - I don't have Mac access, sorry.
  • Errors which prevent files/folders from being moved as expected will print to the command window but will not halt execution (I think); erroneous/missing paths will stop it
  • Lots of informative debug statements will also print to the command window.
  • There is NO UNDO!


// flattens file directories by recursively lifting contents out of subfolders
Function FlattenDir( pathName, recurse, overwrite, [fileFilter, kill] )
    string pathName         // string name of existing path; use NewPath or Misc->New Path...
    variable recurse        // how many levels to flatten? use -1 for ALL, 0 does nothing
    variable overwrite      // nonzero to overwrite files with existing names
    string fileFilter       // optional: provide filter to only lift certain file types
                    //  see <fileTypeOrExtStr> parameter from IndexedFile for OK formats
                    //  defaults to "????"=ALL FILES if format is not acceptable (no warnings!!)
    variable kill           // optional: specify how subfolders are handled after flattening
                    //  negative #s: leave subfolder structure alone (flattens files only)
                    //  0: emptied subfolders deleted only (default)
                    //  1: all subfolders deleted (and remaining contents!!)
                    //  2: do not delete any subfolders
    PathInfo $pathName
    If ( !V_flag )
        print "FlattenDir: path <"+pathName+"> could not be found - aborting."
        return -1
    endif
    GetFileFolderInfo/D/Q/Z S_path
    If ( V_flag )
        print "FlattenDir: directory specified by path <"+pathName+"> could not be found - aborting."
        return -1
    endif
   
    variable i, j, status
    string here = RemoveEnding(S_path,":"), folderList, filesIn, foldersIn, thisFile, thisFolder, tmpPN, subdir, delcmd
    If ( ParamIsDefault(fileFilter) || !GrepString(fileFilter, "^[.]{1}[a-zA-Z0-9]*$|^[a-zA-Z0-9]{4}$|^[?]{4}$") )
        fileFilter = "????"     // ensure fileFilter is an acceptable value
    endif

    if ( recurse )  // do nothing if no more recursive levels
        folderList = IndexedDir( $pathName, -1, 1 )
        for (i=0; i<ItemsInList(folderList); i+=1)      // for each subfolder found in here...
            subdir = StringFromList(i, folderList)
            tmpPN = UniqueName("TmpPath", 12, i)
            NewPath/Q/O $tmpPN, subdir
            // [positive <recurse> eventually stops at 0; negative <recurse> stops at no more subfolders]
            status = FlattenDir(tmpPN, (recurse-1), overwrite, fileFilter=fileFilter, kill=kill)        // ...attempt to flatten it...
            if ( status )
                return status  
            endif
           
            filesIn = IndexedFile( $tmpPN, -1, fileFilter)  // ...pull out all files matching filter...
            for (j=0; j<ItemsInList(filesIn); j+=1)
                thisFile = StringFromList(j, filesIn)
                try
                    if (overwrite)
                        MoveFile/D/O/Z (subdir+":"+thisFile) as here
                        AbortOnValue V_flag, 1
                    else
                        MoveFile/D/Z (subdir+":"+thisFile) as here
                        If ( V_flag )                                   //
                            print "SKIP FILE",thisFile,"BECAUSE NO OVERWRITE" // DEBUG
                        endif                                       //
                    endif
                catch
                    print "FlattenDir: could not move file <"+subdir+":"+thisFile+"> for unknown reason"
                endtry
                print "MOVE FILE",thisFile,"FROM",subdir,"TO",here // DEBUG
            endfor
            If ( kill < 0 )
                KillPath $tmpPN
                continue        // ...and skip the rest if ignoring subfolder structure. Otherwise
            endif

            foldersIn = IndexedDir( $tmpPN, -1, 1 ) // ...pull any subsubfolders out of this subfolder...
            for (j=0; j<ItemsInList(foldersIn); j+=1)
                thisFolder = StringFromList(j, foldersIn)
                try
                    MoveFolder/D/Z thisFolder as here
                    AbortOnValue V_flag, 1
                    print "MOVE DIR",thisFolder,"FROM",subdir,"TO",here // DEBUG
                catch
                    print "FlattenDir: could not move folder <"+thisFolder+"> for unknown reason"
                endtry
            endfor
            switch (kill)       // ... then...
                case 2:         // ...do not delete this subfolder
                    break
                default:
                case 0:         // ...do not delete this subfolder if it has files in it
                    If ( ItemsInList(IndexedFile($tmpPN, -1, "????")) )
                        print "SKIP DIR",subdir,"BECAUSE NOT EMPTY" // DEBUG
                        break
                    endif
                case 1:         // ...delete this subfolder
                    strswitch ( IgorInfo(2) )
                        case "Windows":
                            try
                                delcmd = "recycle.exe -f "+ParseFilePath(5, subdir, "\\", 0, 0)
                                ExecuteScriptText/Z delcmd
                                AbortOnValue V_flag, 1
                                print "RECYCLE DIR",subdir // DEBUG
                            catch
                                print "FlattenDir: could not recycle folder <"+subdir+"> for unknown reason"
                            endtry
                            break
                        case "Macintosh":
                            try // >>   this block adapted from work by aclight via IgorExchange.com
                                string posixPath = ParseFilePath(5, subdir, "/", 0, 0)
                                if (strlen(posixPath) > 0)
                                    sprintf delcmd, "tell application \"Finder\"  to move ((POSIX file \"%s\") as alias) to trash", posixPath
                                    ExecuteScriptText/Z delcmd
                                    AbortOnValue V_flag, 1             
                                endif
                            catch
                                print "FlattenDir: could not move folder <"+subdir+"> to trash for unknown reason"         
                            endtry  // <<
                            break
                    endswitch
            endswitch
            KillPath $tmpPN
        endfor
    endif
    return 0
End
cmdutils.zip (124.67 KB)

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More