Moving folders with the files with exceptions - powershell

I'm trying to move data from the "file" folder and exclude the Users folder. but in the end, the exception does not work.
Move-Item -Path $env:SystemDrive\$env:computername\File\* -exclude $env:SystemDrive\$env:computername\File\Users\* -Destination $env:SystemDrive\UserOld\
It is necessary to transfer the data and exclude the Users folder.

I tried using move-item in order to move folders while excluding a single folder and it doesnt seem like you need to include the entire path in the exclude.
I tried this:
Move-Item -Path C:\Users\D.Baier\Desktop\testenvironment\Source -Exclude mit-1 -Destination C:\Users\D.Baier\Desktop\testenvironment\Target\
and it seemed to work perfectly, just threw an error which seems to be a known issue, at least as far as I understand it.
The error was the following btw:
Move-Item : The Element cannot be moved, since the Element, located at "C:\Users\D.Baier\Desktop\testenvironment\Source\mit-1" does not exist.
In Line:1 Charakter:1
+ Move-Item -Path C:\Users\D.Baier\Desktop\testenvironment\Source \* -Exclu ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Move-Item], PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.MoveItemCommand
Hope that was helpful!
Edit: Sorry, my PC is set to German. I translated the error message as well as I can, however I doubt it is the exact same one one would get if you were to run this code on an English machine. I also apologize for any spelling mistakes I may have made.

First thing I want to touch on is how you're creating your paths. You shouldn't create them the way you are, you should instead use the Join-Path command :)
This is how I might approach your current problem:
#Creating path to source folder
$source = Join-Path -Path "$env:SystemDrive\$env:COMPUTERNAME" -ChildPath "File"
#Creating path to destination folder
$destination = Join-Path -Path $env:SystemDrive -ChildPath "UserOld"
#Looping through all the folders in the source
Get-ChildItem -Path $source -Directory -Recurse | ForEach-Object{
#Only moving the folder if the name isn't "Users"
if ($_.Name -ne "Users"){
Write-Host "Currently Moving: $_.Name"
#Copy-Item -Path $_.FullName -Destination $destination
}
}
Let me know how you get on, I've left the actual moving part commented out as I would encourage you to do more testing :)

carelessness
#Creating path to source folder
$source = Join-Path -Path "$env:SystemDrive\$env:COMPUTERNAME" -ChildPath "\File\C$\"
#Creating path to destination folder
$destination = Join-Path -Path $env:SystemDrive -ChildPath "UserOld"
$h = "Users", "Windows", "ProgramData"
#Looping through all the folders in the source
Get-ChildItem -Path $source -Recurse | ForEach-Object{
#Only moving the folder if the name isn't "Users"
if ($_.Name -ne "$h"){
Write-Host "Currently Moving: $_.Name"
}
Move-Item -Path $_.FullName -Exclude $h -Destination $destination
}
This option moves the Users folder without content.
For this reason, the combined answers.
#Creating path to destination folder
$destination = Join-Path -Path $env:SystemDrive -ChildPath "UserOld"
#Move with the exception
Move-Item -Path $env:SystemDrive\$env:computername\USMT\File\C$\* -Exclude "Users", "Windows","ProgramData","Program Files","Program Files (x86)" -Destination $destination

Related

ForEach-Object to look in subfolders

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.

Powershell access Denied

I've got this function, that move a directory to inside another directory, but always gives me the access denied error.
Any idea what am i doing wrong?
Get-ChildItem C:\Scripts\MetadataExport -Directory | ForEach-Object {
# this will look for a 4-12 digit number in the directory name
If ($_.Name -match '(?:\b|\D)(\d{4,12})(?:\b|\D)') {
$destPath = Join-Path $_.Parent.Fullname $Matches[1]
If (-not (Test-Path -LiteralPath $destPath)) {
New-Item $destPath -ItemType Directory
}
Move-Item -LiteralPath $_.Fullname -Destination $destPath -Force
}
}
the error:
Move-Item : Access to the path 'C:\Scripts\MetadataExport\kwakwala-rosenblum-0172' is denied.
At C:\Users\User\Documents\ScripsPS1\MetadataExport.ps1:170 char:9
Move-Item -LiteralPath $_.Fullname -Destination $destPath -Fo ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : WriteError: (C:\Scripts\Meta...-rosenblum-0172:DirectoryInfo) [Move-Item], IOException
+ FullyQualifiedErrorId : MoveDirectoryItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
So, the goal is to move every folder that part of the name matches with another folder with numeric name.
Ex:
The 'kwakwala-rosenblum-0172' folder needs to move to inside '0172' folder.
Just move if the Literal-Path is the same as folder's name.
I am not sure what you are trying to do but you effectively try to move a folder to a folder with the same name:
$Null = New-Item -Path .\0172 -ItemType Directory -Force
$Null = New-Item -Path .\kwakwala-rosenblum-0172 -ItemType Directory -Force
Get-ChildItem -Directory | Foreach-Object {
if ($_.Name -Match '(?:\b|\D)(\d{4,12})(?:\b|\D)') {
$destPath = Join-Path $_.Parent.Fullname $Matches[1]
Write-Host $_.Fullname '-->' $destPath
}
}
C:\..\0172 --> C:\..\0172
C:\..\kwakwala-rosenblum-0172 --> C:\..\0172
Which is the same as doing this:
Move-Item -LiteralPath .\0172 -Destination .\0172 -force
Move-Item: The process cannot access the file because it is being used by another process.
Meaning that this would likely "resolve" the issue:
if ($_.Fullname -ne $destPath) {
Move-Item -LiteralPath $_.Fullname -Destination $destPath -Force
}
But, I am not sure what your expectation is.
You might want to explain (in the question) how you expect the subfolders to be (re)named.

Need to copy single file to all subfolders recursively

Please can someone help me create a powershell or CMD script, as simple as possible (1 line?) that will do the following...
-Take file (c:\test.txt)
-Copy to ALL subfolders within a given folder, including multiple levels deep
eg, c:\test1\1\2\3\ and c:\test2\6\7\8\
-Without overwriting that file if it already exists
-Not changing ANY existing files. Just adding the original txt file if it doesn't exist.
I've tried a bunch of scripts I found online, changing them, but have been unsuccessful. Either it overwrites existing files, or doesn't go multiple levels deep, or skips all the folders between top/bottom levels, or throws errors. I give up.
Thanks
Matt
How about something like this...
$folders = Get-ChildItem -Recurse -Path C:\temp\1 -Directory
$file = "c:\temp\test.txt"
foreach($folder in $folders){
$checkFile = $folder.FullName + "\test.txt"
$testForFile=Test-Path -Path $checkFile
if(!$testForFile){
Copy-Item $file -Destination $folder.FullName
}
}
Not a one-liner, but here you go:
$rootFolder = 'PATH OF FOLDER CONTAINING ALL THE SUBFOLDERS'
$fileToCopy = 'c:\test.txt'
$fileName = [System.IO.Path]::GetFileName($fileToCopy)
Get-ChildItem -Path $rootFolder -Recurse -Directory | ForEach-Object {
if (!(Test-Path -Path (Join-Path -Path $_.FullName -ChildPath $fileName) -PathType Leaf)) {
Copy-Item -Path $fileToCopy -Destination $_.FullName
}
}

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).

Copy-Item gets IOException when copying to the destination

I'm getting an IOException when Copy-Item copies files to the destination even though the destination is clean. Meaning there are no files in the destination before the copy proceeds. The odd thing is that it does not throw the error with all the files and the file it had an issue with copying does get copied. I'd be a lot easier if it just didn't work.
The code used to do the copying does it such a manner that it creates the current directory structure of the source path at the destination location.
Get-ChildItem $SourcePath -Recurse -Include "$FilePattern" | Foreach-Object {
$destDir = Split-Path ($_.FullName -replace [regex]::Escape($SourcePath), $dstfolder)
if (!(Test-Path $destDir)) {
New-Item -ItemType directory $destDir -ErrorAction SilentlyContinue | Out-Null
sleep 1
}
Copy-Item $_ -Destination $destDir -Force -ErrorAction Ignore -Verbose | Write-Output
}
The actual error message is
VERBOSE: Performing operation "Copy File" on Target "Item: \\EPM111242ND\C$\
HypStaging\Objects\Applications\Vision\Plan1\Plan1.otl Destination:
C:\Users\epmadmin\Documents\Backups\Monday\Full\Objects\Applications\Vision\
Plan1\Plan1.otl". The process cannot access the file 'C:\Users\epmadmin\
Documents\Backups\Monday\Full\Objects\Applications\Visio n\Plan1\Plan1.otl'
because it is being used by another process.
+ CategoryInfo : NotSpecified: (:) [Copy-Item], IOException
+ FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.CopyItemCommand
+ PSComputerName : localhost
It seems to be the destination file that its having an issue with which doesn't exist until it gets copied. Oddly the file is there when I check it. So I tried -ErrorAction SilentlyContinue and -Ignore and both still produce the error message. I was think of maybe putting in a try catch statement but I'm not sure that'll suppress the message.
try this
Get-ChildItem $SourcePath -Recurse -Include "$FilePattern" | % {
$destDir = Split-Path ($_.FullName -replace [regex]::Escape($SourcePath), $dstfolder);
New-Item -ItemType directory $destDir -Force;
Copy-Item $_.fullname -Destination $destDir -Force
}