I have a loop that runs and creates a bunch of files per folder. The folders are uniquely named based on the order range, so they can change. And the folder must exist or the loop crashes.
I want to create a script that will delete all subfolders and files in each subfolder, from a root dir.
ie
Root = C:\Output\
SubFolder = C:\Output\T1-500\
SubFolder = C:\Output\T501-1010\
SubFolder = C:\Output\T1011-3076\
Then have it create folders as needed on the fly.
I tried:
public void Main()
{
// Deletes subfolders and files in the main folder
EmptyFolder(Dts.Variables["User::FolderName"]);
// Creates new folder on the fly
if (Directory.Exists(Dts.Variables["User::FolderName"].Value = 0))
Dts.TaskResult = Directory.CreateDirectory(Dts.Variables["User::FolderName"]);
}
private void EmptyFolder(DirectoryInfo directoryInfo)
{
foreach (FileInfo file in directoryInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo subfolder in directoryInfo.GetDirectories())
{
EmptyFolder(subfolder);
}
}
It doesn't seem to pick up my package level variables, and won't let me add new ones.
I get the following when I try to use my folder variable:
The error means you are passing in the wrong data type. Try this.
Instead of this:
EmptyFolder(Dts.Variables["User::FolderName"]);
Use this:
String folderName = (string)Dts.Variables["User::FolderName"].Value;
DirectoryInfo di = new DirectoryInfo(folderName);
EmptyFolder(di);
There is probably a way to put all of it on one line but start with that.
As mentioned in comments below, the (string) cast may not be necessary if you just use the Value property - please also try that.
You will probably also need to use your new folderName string variable in other parts of your code.
As I mentioned in the comments you can also just run a command line like this to remove a folder and all subfolders
RD <yourfolder> /S /Q
Related
I have the following code to rename files in the following tree as from 00000001.pdf to the last file with this 8 character left padding, e.g: 00000100.pdf
Folder1
subfolder1
childfolder1
pdffile1
pdffile2
childfolder2
pdffile3
pdffile4
subfolder2
childfolder3
pdffile5
pdffile6
But for some reason in some of those child folders it keeps renaming them with no end.
Some times it just jumps to another number, as if it was an async operation. But if I stop and start again it goes okay until the second next folder, when it messes up again.
But this error only happened within 19 folders.
Indeed their pdf names are different from the others, but I don't see how it is related.
The other files were named something like "DOCUMENT_01" and so on, but these are:
0000000100000001.pdf
0000000200000001.pdf
0000000300000001.pdf
etc
static void Main(string[] args)
{
Console.WriteLine("Digite a pasta 'pai' onde serão buscados pdfs dentro das pastas 'filhas':");
string path = Console.ReadLine();
foreach (string dir in Directory.EnumerateDirectories(path))
{
foreach (string subdir in Directory.EnumerateDirectories(dir))
{
Console.WriteLine($"{dir} - {subdir}");
int n = 1;
foreach (string pdffile in Directory.EnumerateFiles(subdir, "*.pdf", SearchOption.AllDirectories))
{
Console.WriteLine(n.ToString().PadLeft(8, '0') + " " + new FileInfo(pdffile).Length);
File.Move(pdffile, subdir + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
n++;
}
Console.WriteLine("\n\n");
}
}
}
What could be going wrong?
It should await for the File.Move method to end to add the n + 1 and then moving to the next pdffile as a synchronous operation. So why does it jumps numbers after a random time and why it keeps going forever other times?
And just to remember, if I stop the program and start again and put the folder that was messed up as the first one, it goes ok and only when it goes to the next folder, or the folder after next that it start to give me this error again.
Hope that I could make myself clear... Thanks for your attention!
EDIT: will try using FileInfo class to give me the parent folder with the SearchOption.AllDirectories option and exclude this 3 stage loop plus actually working for any kind of tree structure
EDIT2: Tried, worked as a "tree indepent" script but getting the same result with the files name after the first folder... As it's really fast, in 3 seconds it goes from 00000169.pdf to 00006239.pdf in a folder with just 330 items.
As commented already, it is not a good idea to move or rename files “WHILE” the code is enumerating though the list of those files as the posted code appears to do. This will cause obvious problems and you should simply mark the files somehow, then later come back and rename or move them.
More importantly, the big issue related to renaming/moving files is exactly as you describe with your current issue. The problem is that the errors are erratic and not consistent. Making it very difficult to trace. However, the problems you describe are classic trademarks of moving/renaming files while enumerating through those files.
With that said, the best way and easiest way to traverse an unknown number of folder levels given a starting folder is by using recursion. In a lot of cases, recursion can be avoided with some well though out loops, however when we do not know how many levels of folders there are, then, using a simple loop or foreach loop paradigm may be doable, however, you will most likely be adding variables and code that only makes this more complex. This is shown in the current code with the addition of the dir variable to keep track of “when” a different folder is used. Recursion is suited ideally for this situation.
In this case, this recursive method will be called ONCE for each folder and subfolders from a given “starting” folder location. This means that each time this recursive method is called is when a different folder is beginning to be processed. So n would always start at 1 and we do not need to keep track of the current folders path.
So the signature of this method will take a DirectoryFolder object as a “starting” folder. First we create some variables; a FileInfo array pdffiles to hold the pdf files in the given folder; in addition to a DirectoryInfo array foldersInThisFolder to hold all the other folders in this starting folder. Lastly an int n to index the files as the posted code is doing.
Next we get all the pdf files in this “starting” folder. If there are pdf files in this folder, then we loop through those files and process them. Next, we get all the other folders in this “starting” folder. Then start a loop through each folder. For each folder in this collection we will make the recursive call back to this method using the next folder as the “starting” folder, then the whole process continues until the loop through those folders ends.
static void TraverseDirectoryTree(DirectoryInfo startingFolder) {
FileInfo[] pdffiles = null;
DirectoryInfo[] foldersInThisFolder = null;
int n = 1;
Console.WriteLine(startingFolder.FullName);
// get all the pdf files in this folder
try {
pdffiles = startingFolder.GetFiles("*.pdf");
}
catch (Exception e) {
// you may want to catch specific exceptions
// however in this example we do not care what
// the exception is, we will simply ignore this.
// in most cases pdffiles will be null if an exception is thrown
Console.WriteLine(e.Message);
}
if (pdffiles != null) {
foreach (FileInfo pdffile in pdffiles) {
Console.WriteLine(pdffile.FullName + " -> " + n.ToString().PadLeft(8, '0') + " " + pdffile.Length);
//File.Move(pdffile.FullName, pdffile.DirectoryName + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
// add file path to a list of files to rename later?
n++;
}
// start over wiith the sub folders in this folder
foldersInThisFolder = startingFolder.GetDirectories();
foreach (DirectoryInfo dirInfo in foldersInThisFolder) {
TraverseDirectoryTree(dirInfo);
}
}
}
Usage…
Console.WriteLine("Type the folder you want to start with:");
string path = Console.ReadLine();
DirectoryInfo di = new DirectoryInfo(path);
TraverseDirectoryTree(di);
Edit… after further testing it appears that what you are wanting to do is simply “rename” the pdf files. As suggested a simple solution is to save the files that we want to rename, then, after we collect the files we want to rename, we simply loop through those files and rename them. This should eliminate any problems by renaming files while enumerating though the files collection.
To help, I created a Dictionary<string, int> called filesToRename. While recursively looping through all the folders, we will add the full path of each pdf file we want to rename as the Key and the int value n as the Value. After the dictionary is filled we would simply loop through it and rename the files.
private static Dictionary<string, int> filesToRename = new Dictionary<string, int>();
Then replace the commented-out line in the recursive method TraverseDirectoryTree…
//File.Move(pdffile.FullName, pdffile.DirectoryName + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
With…
filesToRename.Add(pdffile.FullName, n);
Then after the dictionary is filled we would loop through it and rename the files, something like…
DirectoryInfo di = new DirectoryInfo(path);
TraverseDirectoryTree(di);
foreach (KeyValuePair<string, int> kvp in filesToRename) {
int index = kvp.Key.ToString().LastIndexOf(#"\");
string dir = kvp.Key.ToString().Substring(0, index);
File.Move(kvp.Key, dir + $"\\{kvp.Value.ToString().PadLeft(8, '0')}.pdf");
}
I am hoping this makes sense…
Answer as Klaus Gütter helped me, I just added .ToList() to the Directory.EnumerateFiles so it made a fixed list first, and then made the foreach for each file
It will rename every pdf within the folder and it's subfolders
Console.WriteLine("Type the folder you want to start with:");
string path = Console.ReadLine();
string dir = "";
int n = 1;
foreach (string pdffile in Directory.EnumerateFiles(path, "*.pdf", SearchOption.AllDirectories).ToList())
{
FileInfo fi = new FileInfo(pdffile);
if (fi.DirectoryName == dir)
{
Console.WriteLine("\t" + n.ToString().PadLeft(8, '0'));
File.Move(pdffile, dir + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
n++;
}
else
{
n = 1;
dir = fi.DirectoryName;
Console.WriteLine("\n\n" + dir);
File.Move(pdffile, dir + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
Console.WriteLine("\t" + n.ToString().PadLeft(8, '0'));
n++;
}
}
I am trying to create a simple “directory/file copy" console application in C#. What I need is to copy all folders and files (keeping the original hierarchy) from one drive to another, like from drive C:\Data to drive E:\Data.
However, I only want it to copy any NEW or MODIFIED files from the source to the destination.
If the file on the destination drive is newer than the one on the source drive, then it does not copy.
(the problem)
In the code I have, it's comparing file "abc.pdf" in the source with file "xyz.pdf" in the destination and thus is overwriting the destination file with whatever is in the source even though the destination file is newer. I am trying to figure out how to make it compare "abc.pdf" in the source to "abc.pdf" in the destination.
This works if I drill the source and destination down to a specific file, but when I back out to the folder level, it overwrites the destination file with the source file, even though the destination file is newer.
(my solutions – that didn’t work)
I thought by putting the “if (file.LastWriteTime > destination.LastWriteTime)” after the “foreach” command, that it would compare the files in the two folders, File1 source to File1 destination, but it’s not.
It seems I’m missing something in either the “FileInfo[]”, “foreach” or “if” statements to make this a one-to-one comparison. I think maybe some reference to the “Path.Combine” statement or a “SearchOption.AllDirectories”, but I’m not sure.
Any suggestions?
As you can see from my basic code sample, I'm new to C# so please put your answer in simple terms.
Thank you.
Here is the code I have tried, but it’s not working.
class Copy
{
public static void CopyDirectory(DirectoryInfo source, DirectoryInfo destination)
{
if (!destination.Exists)
{
destination.Create();
}
// Copy files.
FileInfo[] files = source.GetFiles();
FileInfo[] destFiles = destination.GetFiles();
foreach (FileInfo file in files)
foreach (FileInfo fileD in destFiles)
// Copy only modified files
if (file.LastWriteTime > fileD.LastWriteTime)
{
file.CopyTo(Path.Combine(destination.FullName,
file.Name), true);
}
// Copy all new files
else
if (!fileD.Exists)
{
file.CopyTo(Path.Combine(destination.FullName, file.Name), true);
}
// Process subdirectories.
DirectoryInfo[] dirs = source.GetDirectories();
foreach (DirectoryInfo dir in dirs)
{
// Get destination directory.
string destinationDir = Path.Combine(destination.FullName, dir.Name);
// Call CopyDirectory() recursively.
CopyDirectory(dir, new DirectoryInfo(destinationDir));
}
}
}
You can just take the array of files in "source" and check for a matching name in "destination"
/// <summary>
/// checks whether the target file needs an update (if it doesn't exist: it needs one)
/// </summary>
public static bool NeedsUpdate(FileInfo localFile, DirectoryInfo localDir, DirectoryInfo backUpDir)
{
bool needsUpdate = false;
if (!File.Exists(Path.Combine(backUpDir.FullName, localFile.Name)))
{
needsUpdate = true;
}
else
{
FileInfo backUpFile = new FileInfo(Path.Combine(backUpDir.FullName, localFile.Name));
DateTime lastBackUp = backUpFile.LastWriteTimeUtc;
DateTime lastChange = localFile.LastWriteTimeUtc;
if (lastChange != lastBackUp)
{
needsUpdate = true;
}
else
{/*no change*/}
}
return needsUpdate;
}
Update:
I modified my code with the suggestions above and all went well. It did exactly as I expected.
However, the problem I ran into was the amount of time it took run the application on a large folder. (containing 6,000 files and 5 sub-folders)
On a small folder, (28 files in 5 sub-folders) it only took a few seconds to run. But, on the larger folder it took 35 minutes to process only 1,300 files.
Solution:
The code below will do the same thing but much faster. This new version processed 6,000 files in about 10 seconds. It processed 40,000 files in about 1 minute and 50 seconds.
What this new code does (and doesn’t do)
If the destination folder is empty, copy all from the source to the destination.
If the destination has some or all of the same files / folders as the source, compare and copy any new or modified files from the source to the destination.
If the destination file is newer than the source, don’t copy.
So, here’s the code to make it happen. Enjoy and share.
Thanks to everyone who helped me get a better understanding of this.
using System;
using System.IO;
namespace VSU1vFileCopy
{
class Program
{
static void Main(string[] args)
{
const string Src_FOLDER = #"C:\Data";
const string Dest_FOLDER = #"E:\Data";
string[] originalFiles = Directory.GetFiles(Src_FOLDER, "*", SearchOption.AllDirectories);
Array.ForEach(originalFiles, (originalFileLocation) =>
{
FileInfo originalFile = new FileInfo(originalFileLocation);
FileInfo destFile = new FileInfo(originalFileLocation.Replace(Src_FOLDER, Dest_FOLDER));
if (destFile.Exists)
{
if (originalFile.Length > destFile.Length)
{
originalFile.CopyTo(destFile.FullName, true);
}
}
else
{
Directory.CreateDirectory(destFile.DirectoryName);
originalFile.CopyTo(destFile.FullName, false);
}
});
}
}
}
I'm writing application launcher as a Window Application in C#, VS 2017. Currently, having problem with this piece of code:
if (System.IO.Directory.Exists(extractPath))
{
string[] files = System.IO.Directory.GetFiles(extractPath);
string[] dirs = Directory.GetDirectories(extractPath);
// Copy the files and overwrite destination files if they already exist.
foreach (string s in files)
{
// Use static Path methods to extract only the file name from the path.
var fileName = System.IO.Path.GetFileName(s);
var destFile = System.IO.Path.Combine(oldPath, fileName);
System.IO.File.Move(s, destFile);
}
foreach (string dir in dirs)
{
//var dirSplit = dir.Split('\\');
//var last = dirSplit.Last();
//if (last != "Resources")
//{
var fileName = System.IO.Path.GetFileName(dir);
var destFile = System.IO.Path.Combine(oldPath, fileName);
System.IO.Directory.Move(dir, destFile);
//}
}
}
I'm getting well known error
"The process cannot access the file 'XXX' because it is being used by another process."
I was looking for solution to fix it, found several on MSDN and StackOvervflow, but my problem is quite specific. I cannot move only 1 directory to another, which is Resources folder of my main application:
Here is my explanation why problem is specific:
I'm not having any issues with moving other files from parent directory. Error occurs only when loop reaches /Resources directory.
At first, I was thinking that it's beeing used by VS instantion, in which I've had main app opened. Nothing have changed after closing VS and killing process.
I've copied and moved whole project to another directory. Never opened it in VS nor started via *.exe file, to make sure that none of files in new, copied directory, is used by any process.
Finally, I've restarted PC.
I know that this error is pretty common when you try to Del/Move files, but in my case, I'm sure that it's being used only by my launcher app. Here is a little longer sample code to show what files operation I'm actually doing:
private void RozpakujRepo()
{
string oldPath = #"path\Debug Kopia\Old";
string extractPath = #"path\Debug Kopia";
var tempPath = #"path\ZipRepo\RexTempRepo.zip";
if (System.IO.File.Exists(tempPath) == true)
{
System.IO.File.Delete(tempPath);
}
System.IO.Compression.ZipFile.CreateFromDirectory(extractPath, tempPath);
if (System.IO.Directory.Exists(oldPath))
{
DeleteDirectory(oldPath);
}
if (!System.IO.Directory.Exists(oldPath))
{
System.IO.Directory.CreateDirectory(oldPath);
}
if (System.IO.Directory.Exists(extractPath))
{
string[] files = System.IO.Directory.GetFiles(extractPath);
string[] dirs = Directory.GetDirectories(extractPath);
// Copy the files and overwrite destination files if they already exist.
foreach (string s in files)
{
// Use static Path methods to extract only the file name from the path.
var fileName = System.IO.Path.GetFileName(s);
var destFile = System.IO.Path.Combine(oldPath, fileName);
System.IO.File.Move(s, destFile);
}
foreach (string dir in dirs)
{
//var dirSplit = dir.Split('\\');
//var last = dirSplit.Last();
//if (last != "Resources")
//{
var fileName = System.IO.Path.GetFileName(dir);
var destFile = System.IO.Path.Combine(oldPath, fileName);
System.IO.Directory.Move(dir, destFile);
//}
}
}
string zipPath = #"path\ZipRepo\RexRepo.zip";
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
And now, my questions:
Can it be related to file types (.png, .ico, .bmp) ?
Can it be related to fact, that those resources files are being used like, as, for example .exe file icon in my main application? Or just because those are resources files?
Is there anything else what I'm missing and what can cause the error?
EDIT:
To clarify:
There are 2 apps:
Main Application
Launcher Application (to launch Main Application)
And Resources folder is Main Application/Resources, I'm moving it while I'm doing application version update.
It appeared that problem is in different place than in /Resources directory. Actually problem was with /Old directory, because it caused inifinite recurrence.
Ok so this is my issue currently:
This is my main:
//System prompts user the type of archiving which will direct to the correct directory to set as root
Console.WriteLine("Enter the type of archiving you would like to do?");
string archivetype = Console.ReadLine();
//function that identifies which candidates to archive
Archive(archivetype, 20, 20);
//keep the application in debug mode
Console.ReadKey();
The archive method:
//Archive method determines which directory to search in and how many versions to archive
static void Archive(string archivetype, int pversion, int version)
{
//regex pattern to get folder names of the type #.#.#.#/#. something
Regex reg = new Regex(#"\d+(\.\d+)+");
//setting where to start looking
DirectoryInfo root = new DirectoryInfo(#"C:\Users\jphillips\Desktop\test\parent\ACE-3_0");
var dirs = new List<DirectoryInfo>();
//i want to make a recursive call to all the folders in my root directory to obtain all the folders with the regex pattern above that are not empty and do not have 3 files inside
WalkDirectoryTree(root);
}
finally walk directorytree method
//so im using the walk directory tree on the microsoft website i need a way to have a sort of a global array to keep adding the directories that fit through the patterns mentioned above without resetting itself after each walkdirectorytree call
static void WalkDirectoryTree(System.IO.DirectoryInfo root)
{
DirectoryInfo[] subDirs = null;
// Now find all the subdirectories under this root directory.
subDirs = root.GetDirectories();
foreach (DirectoryInfo dir in subDirs)
{
//dirs is not global so it doesnt work here and i believe if i put a local array that it will reset itself everytime
dirs = root.GetDirectories("*", SearchOption.TopDirectoryOnly).Where(d => reg.IsMatch(d.Name)).ToList();
if()
WalkDirectoryTree(dir);
}
}
so im really lost at this point I want to able to call walkdirectorytree to go through all my folders and subfolders of my directory recursevely to extrract the paths that have the regex pattern and that are not empty and do not have 3 files inside tp give me a list of these folders paths.
You can get all the folders and subfolders in a single call with this overload of GetDirectories.
You pass in a search string - but not a regex unfortunately and SearchOption.AllDirectories as the second argument. You can then pass the results through your regex to find the ones you are interested in.
Hello everyone and well met! I have tried a lot of different methods/programs to try and solve my problem. I'm a novice programmer and have taken a Visual Basic Class and Visual C# class.
I'm working with this in C#
I started off by making a very basic move file program and it worked fine for one file but as I mentioned I will be needing to move a ton of files based on name
What I am trying to do is move .pst (for example dave.pst) files from my exchange server based on username onto a backup server in the users folder (folder = dave) that has the same name as the .pst file
The ideal program would be:
Get files from the folder with the .pst extension
Move files to appropriate folder that has the same name in front of the .pst file extension
Update:
// String pstFileFolder = #"C:\test\";
// var searchPattern = "*.pst";
// var extension = ".pst";
//var serverFolder = #"C:\test3\";
// String filename = System.IO.Path.GetFileNameWithoutExtension(pstFileFolder);
// Searches the directory for *.pst
DirectoryInfo sourceDirectory = new DirectoryInfo(#"C:\test\");
String strTargetDirectory = (#"C:\test3\");
Console.WriteLine(sourceDirectory);
Console.ReadKey(true);>foreach (FileInfo file in sourceDirectory.GetFiles()) {
Console.WriteLine(file);
Console.ReadKey(true);
// Try to create the directory.
System.IO.Directory.CreateDirectory(strTargetDirectory);
file.MoveTo(strTargetDirectory + "\\" + file.Name);
}
This is just a simple copy procedure. I'm completely aware. The
Console.WriteLine(file);
Console.ReadKey(true);
Are for verification purpose right now to make sure I'm getting the proper files and I am. Now I just need to find the folder based on the name of the .pst file(the folder for the users are already created), make a folder(say 0304 for the year), then copy that .pst based on the name.
Thanks a ton for your help guys. #yuck, thanks for the code.
Have a look at the File and Directory classes in the System.IO namespace. You could use the Directory.GetFiles() method to get the names of the files you need to transfer.
Here's a console application to get you started. Note that there isn't any error checking and it makes some assumptions about how the files are named (e.g. that they end with .pst and don't contain that elsewhere in the name):
private static void Main() {
var pstFileFolder = #"C:\TEMP\PST_Files\";
var searchPattern = "*.pst";
var extension = ".pst";
var serverFolder = #"\\SERVER\PST_Backup\";
// Searches the directory for *.pst
foreach (var file in Directory.GetFiles(pstFileFolder, searchPattern)) {
// Exposes file information like Name
var theFileInfo = new FileInfo(file);
// Gets the user name based on file name
// e.g. DaveSmith.pst would become DaveSmith
var userName = theFileInfo.Name.Replace(extension, "");
// Sets up the destination location
// e.g. \\SERVER\PST_Backup\DaveSmith\DaveSmith.pst
var destination = serverFolder + userName + #"\" + theFileInfo.Name;
File.Move(file, destination);
}
}
System.IO is your friend in this case ;)
First, Determine file name by:
String filename = System.IO.Path.GetFileNameWithoutExtension(SOME_PATH)
To make path to new folder, use Path.Combine:
String targetDir = Path.Combine(SOME_ROOT_DIR,filename);
Next, create folder with name based on given fileName
System.IO.Directory.CreateDirectory(targetDir);
Ah! You need to have name of file, but with extension this time. Path.GetFileName:
String fileNameWithExtension = System.IO.Path.GetFileName(SOME_PATH);
And you can move file (by File.Move) to it:
System.IO.File.Move(SOME_PATH,Path.Combine(targetDir,fileNameWithExtension)
Laster already show you how to get file list in folder.
I personally prefer DirectoryInfo because it is more object-oriented.
DirectoryInfo sourceDirectory = new DirectoryInfo("C:\MySourceDirectoryPath");
String strTargetDirectory = "C:\MyTargetDirectoryPath";
foreach (FileInfo file in sourceDirectory.GetFiles())
{
file.MoveTo(strTargetDirectory + "\\" + file.Name);
}