PowerShell - bulk move of some subfolders to new server - powershell

I have been asked to move around 200 folders to a new server, but I'm not sure how to script it, or if it is possible.
I have a duplicate folder structure at the destination, but the problem is I have to move only 1 subfolder and its contents in each of the parent folders across. Looks like this:
FolderA
Folder1
Folder2
FolderB
Folder1
Folder2
Is it possible to move only 'Folder1' from Folders a-z and place 'Folder1' in its corresponding new parent folder?

I'd use Robocopy, particularly if you want to preserve the ownership and permissions.

I would be very tempted to use RoboCopy to do the copying, because you can set it to bring over the original file created and modified dates and times, and security permissions.
Although it can't do the Folder1 thing natively. So I would be looking at using PowerShell to generate a batch file of RoboCopy commands, then running that. e.g. something that looks like this, although I haven't tested it.
$sourceFolder = "\\server1\share"
$destFolder = "\\server2\share"
foreach ($folder in (Get-ChildItem -Directory $sourceFolder)) {
"robocopy `"$($sourceFolder)\$($folder)\Folder1`" `"$($destFolder)\$($folder)\Folder1`" /E /COPYALL /DCOPY:T" | Out-File roboscript.bat -Append
}
Then check over, and run roboscript.bat to do the copying.

More a comment on TessellatingHeckler's code than an answer here. Please give any credit to him that you would attribute to this since he had the answer first.
Since you are working with outputting strings to a text file you probably want to work with strings. In regards to your ForEach I would like to suggest:
foreach ($folder in (Get-ChildItem -Directory $sourceFolder | Select -ExpandProperty FullName)) {
$TargetFolder = $Folder -replace [regex]::Escape($sourceFolder), "$destFolder"
"robocopy `"$Folder\Folder1`" `"$TargetFolder\Folder1`" /E /COPYALL /DCOPY:T" | Out-File roboscript.bat -Append
}
That selects the full path for the folder in question as a part of the ForEach. Then it declares a variable as the target in which it replaces the source path with the destination path (source is escaped since this is a RegEx match). This likely makes it more flexible.
Also, you don't need to make subexpressions this way since it's just a string that you're referencing and not a [FileInfo] object.
Lastly, I thought I'd add that you can use two consecutive double quotes instead of escaping them if you prefer, though I would suggest escaping them as TH suggested.

Related

Moving files to folders based on a filename

Howdy all of you smart people! I am a bit out of my depth and thought I would ask a question to see if someone with a higher IQ could at least point me in the right direction.
I have a share with a lot of single lvl subfolders all with similar names in format (xxxxxx_x)
Inside of each folder there are several files:
half will start with the same prefix "xxxxxx_x" as the parent folder
other half has another prefix of 10 digits followed by the parent folder prefix:"yyyyyyyyyy-xxxxxx_x"
I was asked to move files to a new location where I have already scripted a deployment of same folder structure but of course the new ask is for the files to be split based on the above two naming conventions.
TL;DR:
So to summarize, how can I move a batch of files from multiple subfolders to other location based on a part of the filename? (prefix)
typical example:
c:\share\123456_2\123456_2-filename.xls should go to d:\share\123456_2
while
c:\share\123456_2\1234567890-123456_2-filename.xls should go to E:\share\123456_2
I could get the files moved and folders created (if missing) using below but how do I only target files that start with the same string (0,8)?
dir | %{
$id = $_.Name.SubString(0,8);
if(-not (Test-Path $id)) {mkdir $id};
mv $_ "D:\share\$_";}
Well, one way to do this is using the -Split operator, or method to get just the first part of the broken string and check to see if it contains the underscore character (_); since that seems to follow the naming scheme.
Get-ChildItem |
ForEach-Object -Process {
$destination = $_.Name.Split('-')[0]
if ($destination -match "_") {
Move-Item -LiteralPath $_.FullName -Destination "D:\share\$destination" -WhatIf
}
}
Given that you're not looking to move the other files, this will only move the files that have the same beginning.
Remove the -WhatIf safety common parameter once you've dictated the results are what you're after.

Apply a file to multiple folders using better PowerShell script

I'm working on a project where I have to apply a file to multiple folders every so often. I'm trying to learn some PowerShell commands to make this a little easier. I came up with the following script, which works, but I feel that this is too verbose and could be distilled down with a better script:
[string]$sourceDirectory = "C:\Setup\App Folder Files\*"
# Create an array of folders
$destinationDirectories = #(
'C:\Users\GG_RCB1\Documents\',
'C:\Users\GG_RCB2\Documents\',
'C:\Users\LA_RCB1\Documents\',
'C:\Users\PR_RCB1\Documents\',
'C:\Users\PQ_RCB1\Documents\',
'C:\Users\PQ_RCB2\Documents\',
'C:\Users\XC_RCB1\Documents\',
'C:\Users\XC_RCB2\Documents\',
'C:\Users\XC_RCB3\Documents\',
'C:\Users\XC_RCB4\Documents\',
'C:\Users\XC_RCB5\Documents\',
'C:\Users\XC_RCB6\Documents\',
'C:\Users\XC_RCB7\Documents\',
'C:\Users\XC_RCB8\Documents\')
# Perform iteration to create the same file in each folder
foreach ($i in $destinationDirectories) {
Copy-item -Force -Recurse -Verbose $sourceDirectory -Destination $i
}
I go into this process knowing that every folder in the User folder area is going to have the same format: _RCB<#>\Documents\
I know that I can loop through those files using this code:
Get-ChildItem -Path 'C:\Users'| where-object {$_.Name -match "^[A-Z][A-Z]_RCB"}
What I'm not sure how to do is to how, within that loop, drill down to the Documents folder and do the copy. I want to avoid having to keep updating the array from the first code sample, particularly when I know the naming convention of the subfolders in the Users folder. I'm just looking for a cleaner way to do this.
Thanks for any suggestions!
Ehh, I'll go ahead and post what I had in mind as well. Not to take away from #Mathias suggestion in the comments, but to offer my solution, here's my take:
Get-ChildItem -Path "C:\users\[A-Z][A-Z]_RCB*\documents" |
Copy-Item -Path $sourceDirectory -Destination { $_.FullName } -Recurse -WhatIf
Since everyone loves the "One-Liners" that can accomplish your needs. Get-ChildItem accepts wildcard-expressions in it's path which let's us accomplish this in one go. Given that your directories are...
consistent with the same naming pattern,
[A-Z][A-Z]_*
and the folder destination is the same.
Documents
Luckily, Copy-Item also has some cool features on it's own such as being able to use a script block that will allow the passing of $_.FullName property as it's destination, while they are passed down the pipeline one at a time.
Remove the -WhatIf common parameter when you've dictated the results are what you're after.

PowerShell Copy and Rename Files in the same Folder

I have a bunch of file which I like to copy or duplicate in the same folder but renaming part of the filename during the copying...
Ex.
Copying these files
¸Ó¸®30_³²_0.pal
¸Ó¸®30_³²_1.pal
¸Ó¸®30_³²_2.pal
¸Ó¸®30_³²_3.pal
¸Ó¸®30_³²_10.pal
¸Ó¸®30_³²_11.pal
¸Ó¸®30_³²_12.pal
But must be renamed to these
¸Ó¸®31_³²_0.pal
¸Ó¸®31_³²_1.pal
¸Ó¸®31_³²_2.pal
¸Ó¸®31_³²_3.pal
¸Ó¸®31_³²_10.pal
¸Ó¸®31_³²_11.pal
¸Ó¸®31_³²_12.pal
Also I want an input of what to copy and what it will be renamed to...
I only need to input 30_ for the files to copy then input 31_ or 41_ for the copied files..
If it's not possible for input.. I can do with hard coded value...
Update:
I found a code that copies and renames the files..
Get-ChildItem '*30_*.pal' -recurs | % {
$copyto = $_.FullName -replace "30_","41_"
Copy-Item $_.FullName $copyto
}
The input is what's missing now.. and I don't know how to do it...
This should replace the each file if your get-childitem is working properly
Get-ChildItem '*30_*.pal' -Recurse |% {$_.Replace('30','31')}
Ranadip Dutta's answer is good, but the replace might give issues if the second set of numbers ever contains a 30...for example, if there are more files in the group and you have one:
¸Ó¸®30_³²_30.pal
It would get renamed to:
¸Ó¸®31_³²_31.pal
In order to avoid this, use more identifying characters in the Replace to be sure that you are replacing the number in the correct location in the file, like this:
Get-ChildItem '*30_*.pal' -Recurse |% {$_.Replace('®30','®31')}

Copying files defined in a list from network location

I'm trying to teach myself enough powershell or batch programming to figure out to achieve the following (I've had a search and looked through a couple hours of Youtube tutorials but can't quite piece it all together to figure out what I need - I don't get Tokens, for example, but they seem necessary in the For loop). Also, not sure if the below is best achieved by robocopy or xcopy.
Task:
Define a list of files to retrieve in a csv (file name will be listed as a 13 digit number, extension will be UNKNOWN, but will usually be .jpg but might occasionally be .png - could this be achieved with a wildcard?)
list would read something like:
9780761189931
9780761189988
9781579657159
For each line in this text file, do:
Search a network folder and all subfolders
If exact filename is found, copy to an arbitrary target (say a new folder created on desktop)
(Not 100% necessary, but nice to have) Once the For loop has completed, output a list of files copied into a text file in the newly created destination folder
I gather that I'll maybe need to do a couple of things first, like define variables for the source and destination folders? I found the below elsewhere but couldn't quite get my head around it.
set src_folder=O:\2017\By_Month\Covers
set dst_folder=c:\Users\%USERNAME&\Desktop\GetCovers
for /f "tokens=*" %%i in (ISBN.txt) DO (
xcopy /K "%src_folder%\%%i" "%dst_folder%"
)
Thanks in advance!
This solution is in powershell, by the way.
To get all subfiles of a folder, use Get-ChildItem and the pipeline, and you can then compare the name to the insides of your CSV (which you can get using import-CSV, by the way).
Get-ChildItem -path $src_folder -recurse | foreach{$_.fullname}
I'd personally then use a function to edit the name as a string, but I know this probably isn't the best way to do it. Create a function outside of the pipeline, and have it return a modified path in such a way that you can continue the previous line like this:
Get-ChildItem -path $src_folder -recurse | foreach{$_.CopyTo (edit-path $_.fullname)}
Where "edit-directory" is your function that takes in the path, and modifies it to return your destination path. Also, you can alternatively use robocopy or xcopy instead of CopyTo, but Copy-Item is a powershell native and doesn't require much string manipulation (which in my experience, the less, the better).
Edit: Here's a function that could do the trick:
function edit-path{
Param([string] $path)
$modified_path = $dst_folder + "\"
$modified_path = $path.substring($src_folder.length)
return $modified_path
}
Edit: Here's how to integrate the importing from CSV, so that the copy only happens to files that are written in the CSV (which I had left out, oops):
$csv = import-csv $CSV_path
Get-ChildItem -path $src_folder -recurse | where-object{$csv -contains $_.name} | foreach{$_.CopyTo (edit-path $_.fullname)}
Note that you have to put the whole CSV path in the $CSV_path variable, and depending on how the contents of that file are written, you may have to use $_.fullname, or other parameters.
This seems like an average enough problem:
$Arr = Import-CSV -Path $CSVPath
Get-ChildItem -Path $Folder -Recurse |
Where-Object -FilterScript { $Arr -contains $PSItem.Name.Substring(0,($PSItem.Length - 4)) } |
ForEach-Object -Process {
Copy-Item -Destination $env:UserProfile\Desktop
$PSItem.Name | Out-File -FilePath $env:UserProfile\Desktop\Results.txt -Append
}
I'm not great with string manipulation so the string bit is a bit confusing, but here's everything spelled out.

Renaming files in bulk and in ascending order in CMD

I know this question was already asked by someone but I will ask again.
Can someone tell me how to rename in bulk and in ascending order if possible in CMD. I already tried renaming in powershell but to no avail. It only let me use once and I need to rename another folder files but to no avail. It didn't let it rename files in another folder. This is the code I use in powershell:
$i = 1
Get-ChildItem *.mkv | %{Rename-Item $_ -NewName ('Haikyuu - {0:D2}.mkv' -f $i++)}
I'm renaming my anime series per folder and some of my copies have 100+ videos. and somehow you could teach me what each code mean (the code that must use in CMD). The ones I've searched can't understand it in layman's term or doesn't tell the user how it's supposed to work. Thank you in advance. by the way, the folder is placed in an external drive.
so from the beginning:
$i= variable for storing the initial value 1
Get-ChildItem = is like "dir" which lists the files and folder under a certain path.
In this case, it is listing all the files which starts with anything but have the extension .mkv
* indicates wildcard.
| = pipeline which passes the output of the first command as an input of the next command.
% = ForEach-Object is iterating each object one by one coming from the pipeline.
$_= Current pipeline object . Here it is taking each object one by one and renaming it using Rename-Item
-NewName = is the parameter of the Rename-Item which asks for the new name to pass.
Hope it clarifies your need.
The reason why I can't rename my video files is there were [brackets] on the filename.
So I use this:
Get-ChildItem -Recurse -Include *.mkv | Rename-Item -NewName { $_.Name.replace("[","").replace("]","").replace("(","").replace(")","") }
Which on the same directories, I can access subfolders too to omit brackets and parethesis. then I proceed using the code above in the question to rename my files in every folder. The Reason why I'm doing the 'renaming' per folder is that, each folder is different anime series. but the code above is working.
if anyone can give me less code than repeating the 'replace' and concatenating it, I will gladly accept and choose that as the best answer. :)
If you use the parameter -LiteralPath for the source, no prior renaming is necessary.
%i = 1
Get-ChildItem *.mkv |
ForEach {Rename-Item -LiteralPath "$_" -NewName ('Haikyuu - {0:D2}.mkv' -f $i++)}
A hint on sorting, I hope the present numbering of the source files has a constant width, otherwise the result is mixed up as an alphabetic sort (which is inherent to ntfs formatted drives) will sort the number 10 in front of 2.
To check this append the parameter -whatif to the Rename-Item command