Create Subfolders using Powershell - powershell

Ok, looking for some assistance with Powershell. I need to create a subfolder with the same name in about 200 folders in a directory. So far I have this:
$folder = NewFolderName
new-item -type directory -path \\servername\directory\directory\$folder -Force
Will this work to create the single folder in all 200 folders?

Try the following code snippet:
$parent = '\\servername\directory'
$folder = 'NewFolderName'
Get-ChildItem -Path $parent -Directory |
ForEach-Object {
New-Item -WhatIf -Type Directory -Path (
Join-Path -Path $_.FullName -ChildPath $folder) -Force
}
Remove the risk mitigation parameter -WhatIf no sooner than debugged…

Related

How to move file from on sub folder to another sub folder also repeat same for other files using PowerShell

I am trying to copy a file from one location to another. The issue is there are multiples folders so am working under plugins folder which again has sub folders in it. I want to search in each folder if it contains MANIFEST.MF.eclipse file if yes, the I have to move that file in its subfolder called META-INF. (Need to do same for all subfolder under plugins)
$current_folder = .\temp\p2\plugins\com.xyz.*\MANIFEST.MF.eclipse -Recurse |
$new_folder = .\temp\p2\plugins\com.xyz.*\META-INF
if (Test-Path -Path $new_folder){
Move-Item -Path $current_folder -Destination -Path $new_folder
}
else{
New-Item -ItemType 'Directory' -Name $new_folder
Move-Item -Path $current_folder -Destination -Path $new_folder
}
OK, so you're looking to search through a given directory for any files that are called "MANIFEST.MF.eclipse", then move them to a sub directory calles \META-INF?
I'm sure there's more 'elegant' solutions, but this works in my limited test
$rootpath = "C:\test\app"
$foldername = "META-INF"
$files = Get-ChildItem $rootpath -Recurse -Filter *MANIFEST.MF.eclipse
foreach($f in $files)
{
$testpath = $f.DirectoryName +'\'+ $foldername
if(!(Test-Path $testpath))
{
mkdir $testpath
}
Move-Item $f.FullName -Destination $testpath
}

PowerShell script to copy jpg files from one folder to another by creating two subfolders with the same name

I am in need of some assistance, I am new to PowerShell and am trying to use it to make some of my work easier. I am writing a PowerShell script to copy JPG files from one location (C:\Pictures\People\People) and moving them to a new location.
The issue is that in this new location I need to create a folder with the same name as the JPG and then another subfolder with the same name again as the JPG.
So I need to move images from C:\Pictures\People\People which I will call JPG_Image to C:\Pictures\JPG_Name\JPG_Name\'JPG_Image'
So far I found and have been working with this:
$SourceFolder = "C:\Pictures\People\People"
$TargetFolder = "C:\Pictures\"
# Find all files matching *.JPG in the folder specified
Get-ChildItem -Path $SourceFolder -Filter *.jpg |
ForEach-Object {
$ChildPath = Join-Path -Path $_.Name.Replace('.jpg','') -ChildPath $_.Name
[System.IO.FileInfo]$Destination = Join-Path -Path $TargetFolder -ChildPath $ChildPath
# Create the directory if it doesn't already exits
if( -not ( Test-Path -Path $Destination.Directory.FullName ) ){
New-Item -ItemType Directory -Path $Destination.Directory.FullName
}
Copy-Item -Path $_.FullName -Destination $Destination.FullName
}
You are making this harder on yourself than needs be.
Some enhancements to your code:
Add switch -File to the Get-ChildItem cmd so you do not also get DirectoryInfo objects
To get the filename without extension, there is a property .BaseName
Join-Path returns a string, no need to cast that into a [System.IO.FileInfo] object
If you add -Force to the New-Item cmd, there is no need to check if a folder already exists, because that will make the cmdlet either create a new folder or return the existing DirectoryInfo object.
Because we don't need that object (and the console output from it), we can just throw that away using $null = New-Item ...
Putting it all together:
$SourceFolder = "C:\Pictures\People\People"
$TargetFolder = "C:\Pictures"
# Find all files matching *.JPG in the folder specified
Get-ChildItem -Path $SourceFolder -Filter '*.jpg' -File |
ForEach-Object {
# Join-Path simply returns a string containing the combined path
# The BaseName property is the filename without extension
$ChildPath = Join-Path -Path $_.BaseName -ChildPath $_.BaseName
$Destination = Join-Path -Path $TargetFolder -ChildPath $ChildPath
# Create the directory if it doesn't already exits
# Using -Force will not give an error if the folder already exists
$null = New-Item -Path $Destination -ItemType Directory -Force
$_ | Copy-Item -Destination $Destination
}

How can I pipe multiple files into Copy-Item and keep the directory structure?

I have a very large directory located at D:\Stuff and I want to create a copy of it at D:\CopyStuff, but I only want to take files with a certain extension as well as keep the folder structure.
Getting the files I want seems simple enough:
$from = "D:\stuff"
$to = "D:\CopyStuff"
$files = Get-ChildItem -Recurse -Path $from -Include *.config, *.txt, *.ini
However, copying the files and keeping the structure is a bit more challenging. I could use a for-loop, but that seems against the very nature of Powershell. Here https://stackoverflow.com/a/25780745/782880, it suggests to do it this way:
Get-ChildItem -Path $sourceDir | Copy-Item -Destination $targetDir -Recurse -Container
But that copies files to D:\CopyStuff with no folders, much less my original structure. What am I doing wrong? I'm using Powershell 5.
try this :
$Source="C:\temp\test1"
$Dest="C:\temp\test2"
$EnableExt=".config", ".txt" , ".ini"
Get-ChildItem $Source -Recurse | % {
$NewPath=$_.FullName.Replace($Source, $Dest)
if ($_.psiscontainer)
{
New-Item -Path $NewPath -ItemType Directory -Force
}
elseif ($_.Extension -in $EnableExt)
{
Copy-Item $_.FullName $NewPath -Force
}
}
First of all, Copy-Item can do it on its own like:
$fromFolder = "C:\Temp\Source"
$toFolder = "C:\Temp\Dest"
Copy-Item -Path $fromFolder -Destination $toFolder -Recurse -Filter *.txt
But, you may not like the result: it will make folder "Source" inside the "Dest" folder, and then copy the structure. I reckon, you need the same files/folders from inside "Source" folder to be copy to the "Dest" folder. Well, it's a bit more complex, but here it is:
$fromFolder = "C:\Temp\Source"
$toFolder = "C:\Temp\Dest"
Get-ChildItem -Path $fromFolder -Directory -Recurse | Select-Object FullName, #{N="NewPath";E={$_.FullName.Replace($fromFolder, $toFolder)}} | ForEach-Object { New-Item -Path $_.NewPath -ItemType "Directory" }
Get-ChildItem -Path $fromFolder -Include "*.txt" -Recurse | Select-Object FullName, #{N="NewPath";E={$_.FullName.Replace($fromFolder, $toFolder)}} | ForEach-Object { Copy-Item -Path $_.FullName -Destination $_.NewPath }
It copies folder structure first, then files.
NB! I do strongly recommend to use absolute paths only. Otherwise, the Replace method may give unexpected results.
Note: The solution below creates analogous target folders only for those source folders that contain files matching the -Include filter, not for all source folders.
You can get away with a single-pipeline solution by combining Get-ChildItem -Name with delay-bind script blocks:
$from = 'D:\stuff'
$to = 'D:\CopyStuff'
Get-ChildItem -Name -Recurse -LiteralPath $from -Include *.config, *.txt, *.ini |
Copy-Item `
-LiteralPath { Join-Path $from $_ } `
-Destination { New-Item -Type Directory -Force (Split-Path (Join-Path $to $_)) }
-Name emits paths relative to the input directory as strings.
Delay-bind script block { Join-Path $from $_ } builds the full input file name from each relative input path.
Delay-bind script block { New-Item -Type Directory -Force (Split-Path (Join-Path $to $_)) } builds the full path of the target directory from the target root path and the relative input path and creates that directory on demand, using a preexisting one if present (-Force).

move files with specific extension to folder in higher hierarchy

All my files are in specific folders:
17\1\1\PRO
17\1\2\PRO
17\2\1\PRO
xx\xx\xx\PRO
17 is the year (so 18 for next year etc)
the first 1 is the folder specifying the case number (can be up to 100).
The second 1 is the sub parts on the case number.
That last 1 has a folder PRO in it where all data resides.
We need to move these files, but the files need to stay inside their respective "PRO" folders.
For example:
a file in 17\1\1\pro\xxx\www\ needs to go to 17\1\1\pro\movies
a file in 17\2\2\pro\xxdfsdf\eeee\ needs to go to 17\2\2\pro\movies.
The movies folder should get created if there are files to move.
I need to get a part of the full name of a file and move the file there to the "movie" folder. The problem is I do not know how to split the full name, add \movies to it and move the files there.
This is my code so far:
Get-ChildItem -Path $mypath -Recurse -File -Filter $extension | select $_Fullname |
Move-Item -Force -Destination ($_Fullname.Split("pro"))
If the destination is always "movies subdirectory of the grandparent directory of the file's directory" you can build the destination path relative to the file's location:
Get-ChildItem ... | ForEach-Object {
$dst = Join-Path $_.Directory '..\..\movies'
if (-not (Test-Path -LiteralPath $dst -PathType Container)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Move-Item $_.FullName -Destination $dst
}
If the PRO directory is your anchor you could use a regular expression replacement like this instead:
Get-ChildItem ... | ForEach-Object {
$dst = $_.Directory -replace '^(.*\\\d+\\\d+\\\d+\\PRO)\\.*', '$1\movies'
if (-not (Test-Path -LiteralPath $dst -PathType Container)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Move-Item $_.FullName -Destination $dst
}
If you don't know how many directories there are, I would do something like this:
Get-ChildItem -Path $mypath -Recurse -File -Filter $extension | ForEach-Object {
if ($_.FullName.IndexOf('\PRO\') -gt 0) {
$Destination = Join-Path -Path $_.FullName.Substring(0,$_.FullName.IndexOf('\PRO\') + 5) -ChildPath 'movies';
New-Item $Destination -ItemType Directory -ea Ignore;
$_ | Move-Item -Destination $Destination;
} else {
throw ("\PRO\ path not found in '$($_.FullName)'");
}
}
This will work fine as long as your paths only have \pro\ once. If they have it more than once like customer\pro\17\pro\17\1\1\pro\xx\yy\zz\www and you need the last index, then use $_.FullName.LastIndexOf('\pro\').
If you've got \pro\ directories both before and after the directory that .\pro\movies\ is in, well, you're in trouble. You'll probably have to find a different point of reference.
With a set of folders
17\1\1\PRO
17\1\2\PRO
17\2\1\PRO
You could try the following
$RootPaths = Get-ChildItem -Path C:\folder\*\*\*\pro
$RootPaths will then contain all 3 paths mentioned above and the code below will move all files to the appropriate directory.
ForEach( $Path in $RootPaths)
{
$Movies = Join-Path $Path -Child "Movies"
If( -not (Test-Path $Movies ) ) { New-Item -Path $Movies -ItemType Directory }
Get-ChildItem -Path $Path -Recurse -File -Filter $Extension |
Move-Item -Path $_.FullName -Destination "$( $Path )\Movies"
}
This way it doesn't matter how many levels down your files are. They always get moved to the same directory.

Copy-item Files in Folders and subfolders in the same directory structure of source server using PowerShell

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
}