I have a small script that can successfully copy all the files from folders and subfolders and append the creation time, but the files in the subfolders do not have the creation time appended to their names.
How can I append the creation date to all files in a folder and the subfolders?
My current script is:
$path = "C:\test1"
$destination = "C:\test2"
Get-ChildItem -path $path | ForEach-Object{
$newname = $_.CreationTime.toString("yyyy-MM-dd") + $_.BaseName +$_.Extension
(Copy-Item -Recurse -Path $_.FullName -Destination ( Join-Path $destination $newname))
}
You were really close, but the -Recurse switch should have been on Get-ChildItem and within the loop you need to make sure the destination subfolder paths exist.
Try
$source = "C:\test1"
$destination = "C:\test2"
Get-ChildItem -Path $source -File -Recurse | ForEach-Object {
# create the new target folderpath for the copy
$targetPath = Join-Path -Path $destination -ChildPath $_.DirectoryName.Substring($source.Length)
# make sure the target path exists, if not create it
$null = New-Item -ItemType Directory -Path $targetPath -Force
# create a new filename with CreationDate prefixed
$newName = '{0:yyy-MM-dd}{1}{2}' -f $_.CreationTime, $_.BaseName, $_.Extension
# copy the file
$_ | Copy-Item -Destination (Join-Path -Path $targetPath -ChildPath $newname) -Force
}
While you could create your own recursive method to copy files and rename them as you go, it would be easier to use Copy-Item recursively and rename the files and folders afterwards:
$Source = "src"
$Destination = "dst"
Copy-Item -Recurse $Source $Destination
foreach ($Item in (Get-ChildItem -Recurse -File $Destination)) {
Rename-Item $Item ($Item.Name + "-" + $Item.CreationTime.toString("yyyy-MM-dd"))
}
Im looking to move files based on the last half of the filename. Files look like this
43145123_Stuff.zip
14353135_Stuff.zip
2t53542y_Stuff.zip
422yg3hh_things.zip
I am only looking to move files that end in Stuff.zip
I have this in PowerShell so far but it only will move files according to the first half of a file name.
#set Source and Destination folder location
$srcpath = "C:\Powershelltest\Source"
$dstpath = "C:\Powershelltest\Destination"
#Set the files name which need to move to destination folder
$filterLists = #("stuff.txt","things")
#Get all the child file list with source folder
$fileList = Get-ChildItem -Path $srcpath -Force -Recurse
#loop the source folder files to find the match
foreach ($file in $fileList)
{
#checking the match with filterlist
foreach($filelist in $filterLists)
{
#$key = $file.BaseName.Substring(0,8)
#Spliting value before "-" for matching with filterlists value
$splitFileName = $file.BaseName.Substring(0, $file.BaseName.IndexOf('-'))
if ($splitFileName -in $filelist)
{
$fileName = $file.Name
Move-Item -Path $($file.FullName) -Destination $dstpath
}
}
}
There seems to be some differences between the state goal and what the code actually does. This will move the files to the destination directory. When you are confident that the files will be moved correctly, remove the -WhatIf from the Move-Item command.
$srcpath = "C:\Powershelltest\Source"
$dstpath = "C:\Powershelltest\Destination"
Get-ChildItem -File -Recurse -Path $srcpath |
ForEach-Object {
if ($_.Name -match '.*Stuff.zip$') {
Move-Item -Path $_.FullName -Destination $dstpath -WhatIf
}
}
Actually this can be written in PowerShell very efficiently (I hope I got the details right, let me know):
Get-ChildItem $srcpath -File -Force -Recurse |
where { ($_.Name -split "_" | select -last 1) -in $filterLists } |
Move-Item $dstpath
Alternatively, if you only want to look for this one particular filter, you can specify that directly, using wildcards:
Get-ChildItem $srcpath -Filter "*_Stuff.zip"
I Have this powershell script “copFiles.ps1” that looks in a txt file "Filestocopy.txt" for a list and copies them to a destination
$source = "C:\Data\Filestocopy.txt"
$destination = "C:\Data\Models"
Get-Content $source | ForEach-Object {copy-item $_ $destination}
It’ll only copy the files if they’re in the same folder as the .ps1 file and it ignores subfolders, how can I get it to look in subfolders of the folder that its in, I gather I need to use the -recurse option but don’t know how to rewrite it so it works.
The .ps1 file is fired by a bat file.
Many thanks
I don't know how fast this will be, but you can give an array as the argument for the -Path parameter of Get-ChildItem add the -Recurse switch to dig out the files in subdirectories and simply pipe them along to Copy-Item. something like:
Get-ChildItem (Get-Content $Source) -Recurse |
Copy-Item -Destination $destination
You may also want to add the -File switch.
Update
Based on your comment I played around with this a a little more:
$source = "C:\Data\Filestocopy.txt"
$Destination = "C:\data\Models"
# Get-ChildItem (Get-Content $Source) -Recurse |
Get-ChildItem (Get-Content $Source) -Recurse -File |
ForEach-Object{
If( $_.Directory.FullName -eq $Destination )
{ # Don't work on files already present in the destination
# when the destination is under the current directory...
Continue
}
$FileNum = $null
$NewName = Join-Path -Path $Destination -ChildPath $_.Name
While( (Test-Path $NewName) )
{
++$FileNum
$NewName = Join-Path -Path $Destination -ChildPath ($_.BaseName + "_" + $FileNum + $_.Extension)
}
Copy-Item $_.FullName -Destination $NewName
}
This will increment the destination file name in cases where a file by that name already exists in the destination. If the destination is under the current directory it will prevent analyzing those files by comparing the path of the file to the destination. Files must have unique names in a given folder so I'm not sure how else it can be handled.
I'm trying to rename a bunch of files in a bunch of subfolders.
I want them to have a 'prefix', that coresponds with the subfolder they are placed in.
Example:
Main folder:
Subfolder1
Subfolder2
Subfolder3
All the files in Subfolder1, should be renamed to "Subfolder1_filename.pdf", and the same for the rest of the folders.
$dir = 'C:\Users\user\desktop\testfolder\'
foreach ($i in $dir){
$foldername = Get-ChildItem $dir -name
cd $newdir
rename-item -NewName {$foldername + "_" + $_.Name}
}
$Files = Get-ChildItem -Path $Dir -Recurse -File
ForEach ($File in $Files)
{
$FolderName = ((Split-Path -Path $File.FullName -Parent) -split '\\')[-1]
$FileName = $File.Name
Rename-Item -Path $File.FullName -NewName "${FolderName}_$FileName"
}
This will accomplish renaming them according to their container, but you would need to utilize Move-Item if you want to move+rename, or something of that nature.
I am struggling really hard to get this below script worked to copy the files in folders and sub folders in the proper structure (As the source server).
Lets say, there are folders mentioned below:
Main Folder: File aaa, File bbb
Sub Folder a: File 1, File 2, File 3
Sub Folder b: File 4, File 5, File 6
Script used:
Get-ChildItem -Path \\Server1\Test -recurse | ForEach-Object {
Copy-Item -LiteralPath $_.FullName -Destination \\server2\test |
Get-Acl -Path $_.FullName | Set-Acl -Path "\\server2\test\$(Split-Path -Path $_.FullName -Leaf)"
}
Output:
File aaa, File bbb
Sub Folder a (Empty Folder)
Sub Folder b (Empty Folder)
File 1, File 2, File 3, File 4, File 5, File 6.
I want the files to get copied to their respective folders (Like the source folders). Any further help is highly appreciated.
This can be done just using Copy-Item. No need to use Get-Childitem. I think you are just overthinking it.
Copy-Item -Path C:\MyFolder -Destination \\Server\MyFolder -recurse -Force
I just tested it and it worked for me.
edit: included suggestion from the comments
# Add wildcard to source folder to ensure consistent behavior
Copy-Item -Path $sourceFolder\* -Destination $targetFolder -Recurse
If you want to mirror same content from source to destination, try following one.
function CopyFilesToFolder ($fromFolder, $toFolder) {
$childItems = Get-ChildItem $fromFolder
$childItems | ForEach-Object {
Copy-Item -Path $_.FullName -Destination $toFolder -Recurse -Force
}
}
Test:
CopyFilesToFolder "C:\temp\q" "c:\temp\w"
one time i found this script, this copy folder and files and keep the same structure of the source in the destination, you can make some tries with this.
# Find the source files
$sourceDir="X:\sourceFolder"
# Set the target file
$targetDir="Y:\Destfolder\"
Get-ChildItem $sourceDir -Include *.* -Recurse | foreach {
# Remove the original root folder
$split = $_.Fullname -split '\\'
$DestFile = $split[1..($split.Length - 1)] -join '\'
# Build the new destination file path
$DestFile = $targetDir+$DestFile
# Move-Item won't create the folder structure so we have to
# create a blank file and then overwrite it
$null = New-Item -Path $DestFile -Type File -Force
Move-Item -Path $_.FullName -Destination $DestFile -Force
}
I had trouble with the most popular answer (overthinking). It put AFolder in the \Server\MyFolder\AFolder and I wanted the contents of AFolder and below in MyFolder. This didn't work.
Copy-Item -Verbose -Path C:\MyFolder\AFolder -Destination \\Server\MyFolder -recurse -Force
Plus I needed to Filter and only copy *.config files.
This didn't work, with "\*" because it did not recurse
Copy-Item -Verbose -Path C:\MyFolder\AFolder\* -Filter *.config -Destination \\Server\MyFolder -recurse -Force
I ended up lopping off the beginning of the path string, to get the childPath relative to where I was recursing from. This works for the use-case in question and went down many subdirectories, which some other solutions do not.
Get-Childitem -Path "$($sourcePath)/**/*.config" -Recurse |
ForEach-Object {
$childPath = "$_".substring($sourcePath.length+1)
$dest = "$($destPath)\$($childPath)" #this puts a \ between dest and child path
Copy-Item -Verbose -Path $_ -Destination $dest -Force
}
Here you go.
Function Backup-Files {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[System.IO.FileInfo[]]$Source,
[Parameter(Mandatory)]
[String]$Destination
)
if (!(Test-Path $Destination)) {[void][System.IO.Directory]::CreateDirectory($Destination)}
ForEach ($File in $Source) {
$SourceRoot = $(Convert-Path $File.PSParentPath).split('\')[0]
$NewFile = $($File.FullName).Replace($SourceRoot,$Destination)
$NewDir = $($File.DirectoryName).Replace($SourceRoot,$Destination)
[void][System.IO.Directory]::CreateDirectory($NewDir)
Copy-Item -Path $File.FullName -Destination $NewFile -Force
}
}
Examples
<#
.SYNOPSIS
Copy FileInfo object or array to a new destination while retaining the original directory structure.
.PARAMETER Source
FileInfo object or array. (Get-Item/Get-ChildItem)
.PARAMETER Destination
Path to backup source data to.
.NOTES
Version (Date): 1.0 (2023-02-04)
Author: Joshua Biddle (thebiddler#gmail.com)
Purpose/Change: Initial script development.
Known Bugs:
.EXAMPLE
Backup-Files -Source $(Get-ChildItem -Path 'C:\Users\*\Documents' -Recurse -Force -Exclude 'My Music','My Pictures','My Videos','desktop.ini' -ErrorAction SilentlyContinue) -Destination "C:\Temp\UserBackup"
.EXAMPLE
Backup-Files -Source $(Get-ChildItem -Path 'C:\Users\*\Desktop' -Exclude "*.lnk","desktop.ini" -Recurse -Force -ErrorAction SilentlyContinue) -Destination "C:\Temp\UserBackup"
#>
I wanted a solution to copy files modified after a certain date and time which mean't I need to use Get-ChildItem piped through a filter. Below is what I came up with:
$SourceFolder = "C:\Users\RCoode\Documents\Visual Studio 2010\Projects\MyProject"
$ArchiveFolder = "J:\Temp\Robin\Deploy\MyProject"
$ChangesStarted = New-Object System.DateTime(2013,10,16,11,0,0)
$IncludeFiles = ("*.vb","*.cs","*.aspx","*.js","*.css")
Get-ChildItem $SourceFolder -Recurse -Include $IncludeFiles | Where-Object {$_.LastWriteTime -gt $ChangesStarted} | ForEach-Object {
$PathArray = $_.FullName.Replace($SourceFolder,"").ToString().Split('\')
$Folder = $ArchiveFolder
for ($i=1; $i -lt $PathArray.length-1; $i++) {
$Folder += "\" + $PathArray[$i]
if (!(Test-Path $Folder)) {
New-Item -ItemType directory -Path $Folder
}
}
$NewPath = Join-Path $ArchiveFolder $_.FullName.Replace($SourceFolder,"")
Copy-Item $_.FullName -Destination $NewPath
}