recursing folders and renaming files with create date added to filename - powershell

I am very much a newbie to powershell but have figured out some of what I need for a project at work. We have test result files (PDF, XLS. TXT, etc) in folders relating to test locations. Most folders have subfolders with files. The boss has mandated that the test date must be appended to the end of the file name (test_results.pdf -> test_results - 2014-05-06.pdf).
My code does get the creation date and appends it, however it only works on the folders in the source folder, also it is appending the creation date of the folder. I don't mind the date in the folder name if all the files ends up in the correct location. The files in the source subfolders are written to the new sub but without creation date appended.
Any help is greatly appreciated. Here's my code:
$SourceDir= 'C:\modem\'
$targetDir = 'C:\test2\'
set-location -path $sourceDir
$files = get-childitem -recurse
foreach ($file in $files)
{
[string]$strippedFileName =[io.path]::GetFileNameWithoutExtension($file);
[string]$extension = [io.Path]::GetExtension($file);
[string]$crtime=$file.CreationTime.toString(' - yyyy-MM-dd');
[string]$sourceFilePath = $file.DirectoryName;
[string]$DestinationFile = $targetDir + $sourcefilepath.trimstart($sourceDir) + "\" + $strippedFileName +$crtime + $extension;
Copy-Item $file.Fullname -Destination $DestinationFile -recurse -Force
}
Thank you,
tom

It sounds like it doesn't work for the files in the subfolders because you're creating subfolders in the destination directory with different names (date appended), but when you try to copy the files, you're using the original subfolder names in the path to the destination, so you're trying to copy them to locations that don't exist.
If that's the case, you might be wondering, then why do the files from the source subfolders get copied to the corresponding renamed subfolders, but without the date appended? Simple: because if you Copy-Item -Recurse a folder, all the contents get copied. The files in the subfolders aren't being copied individually by your foreach loop; they're being copied all at once by the last line of the loop each time you create a new (renamed) subfolder.
You can solve both problems simultaneously by simply exculding directories from the results of Get-ChildItem.
In PowerShell 1 and 2:
$files = Get-Childitem -Recurse | ?{$_.PSIsContainer}
In PowerShell 3 and up:
$files = Get-Childitem -Recurse -File
Also, it looks like you're coming from a background of .NET programming in a strongly type language (I'm guessing C# based on your accent). PowerShell is dynamically typed; there's no need (and no advantage) to casting all your variables as strings. The assigments will make them strings. Also, you don't need [IO.Path] to get the parts of the filename; they're attributes of the FileInfo objects returned by Get-ChildItem.
You can write you loop like this:
foreach ($file in $files)
{
$strippedFileName = $file.BaseName;
$extension = $file.Extension;
$crtime=$file.CreationTime.toString(' - yyyy-MM-dd');
$sourceFilePath = $file.DirectoryName;
$DestinationFile = $targetDir + $sourcefilepath.TrimStart($sourceDir) + "\" + $strippedFileName +$crtime + $extension;
Copy-Item $file.FullName -Destination $DestinationFile -Recurse -Force
}

Related

Copy-Item with overwrite?

Here is a section of code from a larger script. The goal is to recurse through a source directory, then copy all the files it finds into a destination directory, sorted into subdirectories by file extension. It works great the first time I run it. If I run it again, instead of overwriting existing files, it fails with this error on each file that already exists in the destination:
Copy-Item : Cannot overwrite the item with itself
I try, whenever possible, to write scripts that are idempotent but I havn't been able to figure this one out. I would prefer not to add a timestamp to the destination file's name; I'd hate to end up with thirty versions of the exact same file. Is there a way to do this without extra logic to check for a file's existance and delete it if it's already there?
## Parameters for source and destination directories.
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
# Build list of files to sort.
$Files = Get-ChildItem -Path $Source -Recurse | Where-Object { !$_.PSIsContainer }
# Copy the files in the list to destination folder, sorted in subfolders by extension.
foreach ($File in $Files) {
$Extension = $File.Extension.Replace(".","")
$ExtDestDir = "$Destination\$Extension"
# Check to see if the folder exists, if not create it
$Exists = Test-Path $ExtDestDir
if (!$Exists) {
# Create the directory because it doesn't exist
New-Item -Path $ExtDestDir -ItemType "Directory" | Out-Null
}
# Copy the file
Write-Host "Copying $File to $ExtDestDir"
Copy-Item -Path $File.FullName -Destination $ExtDestDir -Force
}
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
You are trying to copy files from a source directory to a sub directory of that source directory. The first time it works because that directory is empty. The second time it doesn't because you are enumerating files of that sub directory too and thus attempt to copy files over themselves.
If you really need to copy the files into a sub directory of the source directory, you have to exclude the destination directory from enumeration like this:
$Files = Get-ChildItem -Path $Source -Directory |
Where-Object { $_.FullName -ne $Destination } |
Get-ChildItem -File -Recurse
Using a second Get-ChildItem call at the beginning, which only enumerates first-level directories, is much faster than filtering the output of the Get-ChildItem -Recurse call, which would needlessly process each file of the destination directory.

Powershell - Adding a Month to all files in a folder

a complete newbie to Powershell here.
What I am attempting to do is using Powershell, adding a month for the creationtime of files in a folder, which is based off the current creation date of the document, as opposed to the get-date system date.
Edit to how I get it to work for single documents:
I managed to get it to work for a specific file using this command:
$(get-item test.txt).creationtime=$(Get-item test.txt).creationtime.AddMonths(1)
Rather than specify each filer individually, I want to do the same as above but for all documents in a folder.
If the question is to add 1 month to the LastWriteTime date of each file, instead of setting the LastWriteTime date to a date 1 month away from the current system date, you can do
foreach ($file in (Get-ChildItem -Path 'PathToWhereTheFilesAre' -File)) {
$file.LastWriteTime = $file.LastWriteTime.AddMonths(1)
}
If it is the current system date plus 1 month you want, you can use any of the answers given already by both Klausen and Venkataraman R
You can go for foreach loop to iterate over files and update the lastwritetime
$FolderName = "c:\dev"
foreach($file in (GEt-childitem -Path $FolderName ))
{
$file.lastwritetime=$(Get-Date).AddMonths(1)
}
First you need to define which files you want to apply this "addmonths". In the code sample I added two parameters you may want to remove, just in case you need them.
Thanks.
# Folder where your files are located
$folder = 'C:\Temp\'
# Create list of files to go thru.
# -Recurse option will navigate in the folders recursively.
# -File can be used so no folders are picked up in your script
$files = Get-ChildItem -Path $folder -Recurse -File
# Iterate file by file in the results
foreach($file in $files)
{
$file.lastwritetime=$(Get-Date).AddMonths(1)
}
Thanks all for your help!
I've used your guidance and created the following code which does exactly what I want it to do:
$folder = "C:\Users\ajames\Powershell"
$files = Get-ChildItem -Path $folder -Recurse -File
foreach($file in $files)
{$file.creationtime=$($file.CreationTime.AddDays(1)) }
Cheers again.

Excluding Parent Directory if Any File is New

My company has individual folders on a share for each project they are working on, and if no files inside one of those folders or its subfolders has been touched in the last six months, I want to move them to an archive location. If any one file within the folder or any of its subfolders have been modified in the last six months, I want to skip the entire parent directory. I'm most of the way there now, but my current iteration only skips the individual files, and I'm not sure how to specify skipping the entire parent. Here is my current script:
$Date = (Get-Date).AddMonths(-6)
$Source = 'C:\Scripts\Source'
$Dest = 'C:\Scripts\Test Target'
Get-ChildItem $Source -File -Recurse | Where {$_.LastWriteTime -lt $Date} | ForEach {
$actualSource = Split-Path $_.FullName
$actualDest = Split-Path $_.FullName.Replace($source,$dest)
robocopy $actualSource $actualDest $_.Name /SEC
}
When using my test directories, I have a folder C:\Scripts\Source\Drivers. The script copies that Drivers folder like I want it to, but if I put a newer file anywhere within that Drivers folder, I want the entire folder to be skipped. Currently, the folder and anything older than six months within the folder are still being copied, and it is just skipping the individual files which are newer.
Please let me know if any more information is needed.
Simply pull back your copy and recurse statement one level up. First you want to iterate through all the parent folders. Then for each parent folder, recurse and check to see if there is any modified files, if there is, then copy the folder:
$Date = (Get-Date).AddMonths(-6)
$Source = 'C:\Scripts\Source'
$Dest = 'C:\Scripts\Test Target'
$ParentFolders = Get-ChildItem $Source -Directory
Foreach($Folder in $ParentFolders){
$NewFiles = Get-ChildItem $Folder -File -Recurse | Where {$_.LastWriteTime -lt $Date}
if($NewFiles.Count -eq 0)
{
#Archive
robocopy $Folder $Dest /SEC
}
}

PowerShell script isn't copying like I want

Right in the beginning I should note that I am a bloody beginner because I can't attend it classes in my grade.
I want to create a PowerShell script which will copy everything from
C:\Users\Robert\Desktop\test(lots of folders)
to
C:\Users\Robert\Desktop\neu(lots of folders with the exact same names as above)\price
As an absolute beginner I thought that it will be ok to replace the variable folder name with $_Name because it is the same name in both but I am obviously wrong and don't know why.
Here is my attempt
Copy-Item "C:\Users\Robert\Desktop\test\$_name\*" -Destination "C:\Users\Robert\Desktop\neu\$_Name\price" -Recurse
It is copying something but in one package in a new folder in "neu".
I can't avoid creating this script because it would take me at least two or three days to do it by hand.
I am also sorry for my poor English skills
Thank you
the $_ represents the current pipeline item. i don't see a pipeline in there ... [grin]
the following works by grabbing every file in the source dir & its subdirs, and copying that structure to the destination dir. it uses Splatting to structure the parameters neatly.
$SourceDir = "$env:TEMP\Apps - Copy"
$DestDir = "$env:TEMP\Apps - Copy - Two"
$CI_Params = #{
LiteralPath = $SourceDir
Destination = $DestDir
Force = $True
Recurse = $True
}
Copy-Item #CI_Params
If my understanding is correct:
$src = 'C:\Users\Robert\Desktop\test'
$dst = 'C:\Users\Robert\Desktop\neu\{0}\price'
Get-ChildItem $src -Directory | ForEach-Object {
Copy-Item -Path "$($_.FullName)\*" -Destination ($dst -f $_.BaseName) -Recurse -Force -WhatIf
}
Remove -WhatIf to actually do it.

Powershell Copy files and folders

I have a PS script which Zips up the previous months logs and names the zip file FILENAME-YYYY-MM.zip
This works
What I now want to do is copy these zip files off to a network share but keeping some of the folder structure. I currently a folder structure similar to the following;
C:\Folder1\
C:\Folder1\Folder2\
C:\Folder1\Folder3\
C:\Folder1\Folder4\Folder5\
There are .zip files in every folder below c:\Folder1
What I want is for the script to copy files from c:\folder1 to \\networkshare but keeping the folder structure, so I should have 3 folders and another subfolder in folder4.
Currently I can only get it to copy the whole structure so I get c:\folder1\... in my \\networkshare
I keep running into issues such as the new folder structure doesn't exist, I can't use the -recurse switch within the Get-ChildItem command etc...
The script I have so far is;
#This returns the date and formats it for you set value after AddMonths to set archive date -1 = last month
$LastWriteMonth = (Get-Date).AddMonths(-3).ToString('MM')
#Set destination for Zip Files
$DestinationLoc = "\\networkshare\LogArchive\$env:computername"
#Source files
$SourceFiles = Get-ChildItem C:\Sourcefiles\*.zip -Recurse | where-object {$_.lastwritetime.month -le $LastWriteMonth}
Copy-Item $SourceFiles -Destination $DestinationLoc\ZipFiles\
Remove-Item $SourceFiles
Sometimes, you just can't (easily) use a "pure PowerShell" solution. This is one of those times, and that's OK.
Robocopy will mirror directory structures, including any empty directories, and select your files (likely faster than a filter with get-childitem will). You can copy anything older than 90 days (about 3 months) like this:
robocopy C:\SourceFiles "\\networkshare\LogArchive\$($env:computername)\ZipFiles" /E /IS /MINAGE:90 *.zip
You can specify an actual date with /MINAGE too, if you have to be that precise.
How about Copy-Item "C:\SourceFiles\" -dest $DestinationLoc\ZipFiles -container -recurse? I have tested this and have found that it copies the folder structure intact. If you only need *.zip files, you first get them, then for each you call Resolve-Path with -Relative flag set and then add the resultant path into Destination parameter.
$oldloc=get-location
Set-Location "C:\SourceFiles\" # required for relative
$SourceFiles = Get-ChildItem C:\Sourcefiles\*.zip -Recurse | where-object {$_.lastwritetime.month -le $LastWriteMonth}
$SourceFiles | % {
$p=Resolve-Path $_.fullname -relative
copy-item $_ -destination "$DestinationLoc\ZipFiles\$p"
}
set-location $oldloc # return back