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 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More