I'm trying to copy all the .dll files from C:\windows in a virtual server to a new virtual server. I've managed to get all the .dll files, however I can't find a way to copy them to the new virtual server and was wondering if anyone might know how to do this with Powershell.
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$server = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the server name with files you want to copy", "Server")
$server2 = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the server name you want files copied to", "Server")
$destinationName = ("\\" + $server2 + '\C$\windows')
$Files = Get-ChildItem -Path ("\\" + $server + '\C$\windows') -recurse | Where {$_.extension -eq ".dll"}
What would I have to do with my $Files variable to copy it to a new VM? I know of the copy-item cmdlet, but unaware of how to use it to move all of this to a new virtual server.
EDIT:
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$server = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the server name with files you want to copy", "Server")
$server2 = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the server name you want files copied to", "Server")
$destinationName = ("\\" + $server2 + '\C$\windows')
$Files = Get-ChildItem -Path ("\\" + $server + '\C$\windows') -recurse | Where {$_.extension -eq ".dll"}
foreach($dll in $Files){
$destinationName +=
cp $dll.fullname $destinationName}
I want to get string of path to be "\$server2\C$\windows\ ..\ .. " for each specific file.
At the moment if the code runs it'll make every file/directory appear as "\$server2\C$\windows" and not get the full path.
You're really nearly there, actually.
$Files = Get-ChildItem... makes $Files become an array of items, and because Powershell was designed to work with objects, you just would use $Files as an argument to Copy-Item. The caveat with this is that, for whatever reason, Copy-Item doesn't use the full path property from the objects obtained with Get-ChildItem (instead it just gets the filenames, so you'd have to be in that directory for it to work), so the easiest way would be this:
foreach($dll in $Files){cp $dll.fullname $destinationName}
To copy while preserving directory structure, you want to take the starting full path and just modify it to reflect the new root directory/server. This can be done in one line similar to the above, but for clarity and readability, I'm expanding it into the following multi-line setup:
foreach($dll in $Files){
$target = $dll.fullname -replace "\\\\$server","\\$server2"
$targetDir = $($dll.directory.fullname -replace "\\\\$server","\\$server2")
if(!(Test-Path $targetDir){
mkdir $targetDir
}
cp $dll.fullname $target
}
To explain, the $target... line takes the full path of the current $dll, say \\SourceServer\C$\windows\some\rather\deep\file.dll, and regex replaces the part of the path that says \\SourceServer and replaces it with \\DestinationServer so that the rest of the path is left intact. That is, it will now be \\TargetServer\C$\windows\some\rather\deep\file.dll. This method eliminates the need for your $destinationName variable.
The Test-Path bit makes sure that the the parent folder of the file exists remotely before copying, otherwise it will fail out.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 days ago.
Improve this question
I'm not a beginner to scripting with PowerShell and have discovered just how powerful and amazing it truly is. I get confused with some things though so I am here seeking help with a few things with the script that I'm in the process of creating. My script manages a few things. It:
• prompt user to select a directory
• recursively moves files that are many levels deep into the parent folder
• deletes all empty folders after the move
•renames the parent folders by removing periods and other "illegal" characters because the program that uses these files will crash if there are any characters besides numbers or letters.
• renames the files to the parent's name.
• exits when finished
The files don't have a file format extension, they're approx 32 characters long and are alphanumeric.
Unfortunately, the script cannot make it past the first step (moving the files) if it is placed in a directory outside of the one that contains the folders and files. If I place it in the root of the directory containing said files and folders, it works flawlessly. If it is ran in another directory containing other files, it will work with the files and folders there after finishing the 1st step which is set using $RootPath, the rest of the script is not using $RootPath and I need to figure out how to edit the code I have to utilize it.
However no matter what I do, I fail. I know I can just run it from the directory containing the files that need to be moved but I intend to release this on a forum that I frequent and want to make sure it is safe for those that use it. ie: I don't want their system getting messed up through carelessness or ignorance.
Full Disclosure: I'm not good at writing code on my own, I find chunks of code offered in forums, test and if it accomplishes what I need, I tweak it to work with my intended use. Most of this code I found here.
How can I get the last ⅔ of my script to use my $RootPath instead of the script's residing directory? I have tried a few things but end up breaking it's functionally and the thing is, in my mind, I see why it's not working but reading the code is where I have a Patrick-Star-drooling moment. This is when I get overwhelmed and take a break or focus on something else that I do understand. I know I need to make the rest of my code utilize $RootPath that gets set when selecting a directory but I can't figure out how to get it to use it.
Additionally, I would like the final step to append "_1" to the file name when there is a naming conflict. I can't seem to figure out how to get this step to carry over from the first step.
Here is a pastebin link of my script. It is a bit long, I have also pasted the code in case that is preferred.
# You need this to use System.Windows.MessageBox
Add-Type -AssemblyName 'PresentationFramework'
Add-Type -AssemblyName System.Windows.Forms
$continue = 'No'
$caption = "'Bulk File Renamer Script' by MyLegzRwheelz."
$message = "Have you read the ENTIRE disclaimer (from the very top, I know, it is a lot) in the console window along the instructions provided and do you agree that you are responsible for your own negligence and anything that can go wrong IF YOU DO NOT FOLLOW MY INSTRUCTIONS PRECISELY? If so, then click 'Yes' to proceed, 'No' to exit."
$continue = [System.Windows.MessageBox]::Show($message, $caption, 'YesNo');
if ($continue -eq 'Yes') {
$characters = "?!'._" # These are the characters the script finds and removes
$regex = "[$([regex]::Escape($characters))]"
$filesandfolders = Get-ChildItem -recurse | Where-Object {$_.name -match $regex}
$browser = New-Object System.Windows.Forms.FolderBrowserDialog
$null = $browser.ShowDialog()
$RootPath = $browser.SelectedPath
# Get list of parent folders in root path
$ParentFolders = Get-ChildItem -Path $RootPath | Where-Object {$_.PSIsContainer}
# For each parent folder get all files recursively and move to parent, append number to file to avoid collisions
ForEach ($Parent in $ParentFolders) {
Get-ChildItem -Path $Parent.FullName -Recurse | Where-Object {!$_.PSIsContainer -and ($_.DirectoryName -ne $Parent.FullName)} | ForEach-Object {
$FileInc = 1
Do {
If ($FileInc -eq 1) {$MovePath = Join-Path -Path $Parent.FullName -ChildPath $_.Name}
Else {$MovePath = Join-Path -Path $Parent.FullName -ChildPath "$($_.BaseName)($FileInc)$($_.Extension)"}
$FileInc++
}
While (Test-Path -Path $MovePath -PathType Leaf)
Move-Item -Path $_.FullName -Destination $MovePath
}
}
$filesandfolders | Where-Object {$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
Rename-Item -path $_.Fullname -newname $New -passthru
}
# For this to work, we need to temporarily append a file extension to the file name
Get-ChildItem -File -Recurse | where-object {!($_.Extension)} | Rename-Item -New {$_.basename+'.ext'}
# Removes alphanumeric subdirectories after moving renamed game into the parent folder
Get-ChildItem -Recurse -Directory | ? { -Not ($_.EnumerateFiles('*',1) | Select-Object -First 1) } | Remove-Item -Recurse
# Recursively searches for the files we renamed to .ext and renames it to the parent folder's name
# ie: "B2080E9FFF47FE2DA382BD55EDFCA2152078AEBD58.ext" becomes "0 day Attack on Earth" and will be
# found in the directory of the same name.
ls -Recurse -Filter *.ext | %{
$name = ([IO.DirectoryInfo](Split-Path $_.FullName -Parent)).Name
Rename-Item -Path $_.FullName -NewName "$($name)"
}
} else {
{Exit}
}
I have tried using $ParentFolders in varying places so that it uses $RootPath as the working directory. I have also tried copy/pasting the "file inc" part in the final step but it is not working. To test this out, create folder, make this your root folder. Within that folder, create additional folders with multiple subfolders and a file with no extension, just create .txt and remove the extension then run the script from the newly created root folder.
Do not run this in a directory with files you care about. This why I am trying to get the rest of the code to use only the directory set at launch. To test it to see if it is working regardless of the scripts location, place the script in another folder then run it. When the explorer dialog pops up (after clicking yes), select this directory. If you place it in the root directory then run it, it works as it should but not in any other director, which is the desired result, to run and work to completion, regardless of the location of the script.
Here is code to add _1 to filename
$filename = "abcdefg.csv"
$lastIndex = $filename.LastIndexOf('.')
$extension = $filename.Substring($lastIndex)
$filename = $filename.Substring(0,$lastIndex)
Write-Host "filename = " $filename ",extension = " $extension
$filename = $filename + "_1" + $extension
$filename
Got another multi-step process I'm looking to streamline. Basically, I'm looking to build a Powershell script to do three things:
Get-Childitem to look for folders with a specific name (we'll call it NAME1 as a placeholder)
For each folder it finds that has the name, I want it to output the full directory to a TXT file (so that in the end I wind up with a text file that has a list of the results it found, with their full paths; so if it finds folders with "NAME1" in five different subdirectories of the folder I give it, I want the full path beginning with the drive letter and ending with "NAME1")
Then I want it to take the list from the TXT file, and copy each file path to another drive and preserve directory structure
So basically, if it searches and finds this:
D:\TEST1\NAME1
D:\TEST7\NAME1
D:\TEST8\NAME1\
That's what I want to appear in the text file.
Then what I want it to do is to go through each line in the text file and plug the value into a Copy-Item (I'm thinking the source directory would get assigned to a variable), so that when it's all said and done, on the second drive I wind up with this:
E:\BACKUP\TEST1\NAME1
E:\BACKUP\TEST7\NAME1
E:\BACKUP\TEST8\NAME1\
So in short, I'm looking for a Get-Childitem that can define a series of paths, which Copy-Item can then use to back them up elsewhere.
I already have one way to do this, but the problem is it seems to copy everything every time, and since one of these drives is an SSD I only want to copy what's new/changed each time (not to mention that would save time when I need to run a backup):
$source = "C:\"
$target = "E:\BACKUP\"
$search = "NAME1"
$source_regex = [regex]::escape($source)
(gci $source -recurse | where {-not ($_.psiscontainer)} | select -expand fullname) -match "\\$search\\" |
foreach {
$file_dest = ($_ | split-path -parent) -replace $source_regex,$target
if (-not (test-path $file_dest)){mkdir $file_dest}
copy-item $_ -Destination $file_dest -force -verbose
}
If there's a way to do this that wouldn't require writing out a TXT file each time I'd be all for that, but I don't know a way to do this the way I'm looking for except a Copy-Item.
I'd be very grateful for any help I can get with this. Thanks all!
If I understand correctly, you want to copy all folders with a certain name, keeping the original folder structure in the destination path and copy only files that are newer than what is in the destination already.
Try
$source = 'C:\'
$target = 'E:\BACKUP\'
$search = 'NAME1'
# -ErrorAction SilentlyContinue because in the C:\ disk you are bound to get Access Denied on some paths
Get-ChildItem -Path $source -Directory -Recurse -Filter $search -ErrorAction SilentlyContinue | ForEach-Object {
# construct the destination folder path
$dest = Join-Path -Path $target -ChildPath $_.FullName.Substring($source.Length)
# copy the folder including its files and subfolders (but not empty subfolders)
# for more switches see https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
robocopy $_.FullName $dest /XO /S /R:0
}
If you don't want console output of robocopy you can silence it by appending 2>&1, so neither stdout nor stderr is echoed
If you want to keep a file after this with both the source paths and the destinations, I'd suggest doing
$source = 'C:\'
$target = 'E:\BACKUP\'
$search = 'NAME1'
$output = [System.Collections.Generic.List[object]]::new()
# -ErrorAction SilentlyContinue because in the C:\ disk you are bound to get Access Denied on some paths
Get-ChildItem -Path $source -Directory -Recurse -Filter $search -ErrorAction SilentlyContinue | ForEach-Object {
# construct the destination folder path
$dest = Join-Path -Path $target -ChildPath $_.FullName.Substring($source.Length)
# add an object to the output list
$output.Add([PsCustomObject]#{Source = $_.FullName; Destination = $dest })
# copy the folder including its files and subfolders (but not empty subfolders)
# for more switches see https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
robocopy $_.FullName $dest /XO /S /R:0
}
# write the output to csv file
$output | Export-Csv -Path 'E:\backup.csv' -NoTypeInformation
I´m a newbie still and learning to create PowerShell scripts to make Life in IT easier.
At present I´m trying to build a script, which runs a certain Microsoft tool, scanning defined network shares in a csv file and creating an JSON output file.
Now as the pattern of this file is always the same like "Report_Username_Hostname.vba.JSON", I would like to append either the scanned directory name or even a range of numbers, fe. "Report_Username_Hostname(100).vba.JSON" or "Report_Username_Hostname(sharename).vba.JSON"
This is neccessaray as after this renaming step, I upload this and other files within this folder to another folder on different server to upload them into a Database.
I planned to run this script in in many different locations on most automatic level and they all copy the their collected files to just one upload folder.
I already tried several options I found somewhere in the deep of the Internet, but I only came to the point where the file was renamed to 0 or similar, but not to expected result.
The Powershell script doing the work is this:
$PSpath = 'C:\temp\FileRemediation\Scripts\'
$Rpath = $PSpath +'..\Reports\1st'
$localshare = $PSpath +'..\temp\1st'
$csvinputs = Import-Csv $PSpath\fileremediation_1st.csv
$uploadshare = '\\PGC2EU-WFSFR01.eu1.1corp.org\upload\'
# This section checks if the folder for scan reports is availabe and if not will create necessary folder.
If(!(test-path $Rpath))
{
New-Item -ItemType Directory -Force -Path $Rpath
}
If(!(test-path $localshare))
{
New-Item -ItemType Directory -Force -Path $localshare
}
Set-Location $Rpath
# This section reads input from configuration file and starts Ms ReadinessReportCreator to scan shares in configuration file.
ForEach($csvinput in $csvinputs)
{
$uncshare = $csvinput.sharefolder
$Executeable = Start-Process "C:\Program Files (x86)\Microsoft xxx\xxx.exe" `
-Argumentlist "-p ""$uncshare""", "-r", "-t 10000", "-output ""$localshare"""`
-Wait
Get-ChildItem -Path $localshare -Filter '*.JSON' | Rename-Item -NewName {$_.FullName+$uncshare}
}
# This section copies the output *.JSON file of the xxx to the share, where I they will be uploaded to DB.
Get-ChildItem -Path $localshare -Filter '*.JSON' | Where {$_.Length -ge 3} | move-item -Destination '$uploadshare'
the fileremediation_1st.csv looks like
sharefolder
\\server\sharename
Can someone please help me on this, I don´t have a clue what I´m doing wrong.
Thank you!
Current error I get is
Rename-Item : Cannot rename the specified target, because it
represents a path or device name. At
C:\temp\FileRemediation\scripts\fileremediation_V2_1st.ps1:28 char:55
+ ... share -Filter '*.JSON' | Rename-Item -NewName {$_.FullName+$uncshare}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
As said before, I would also be fine with a dedicated range of numbers, which is append to the file name "Report_Username_Hostname(100).vba.JSON"
The perfect world would be if I could split the \server\sharename from csv file and append the sharename to my filename.
I think the issue is with:
Rename-Item -NewName {$_.FullName+$uncshare}
Your input file (Get-ChildItem) path is:
$PSpath = 'C:\temp\FileRemediation\Scripts\'
$localshare = $PSpath +'..\temp\1st'
The Rename-Item uses $_.FullName which resolves to something like this:
C:\temp\FileRemediation\Scripts\..\temp\1st\MyFile.JSON
The variables then contain:
$_.FullName = C:\temp\FileRemediation\Scripts\..\temp\1st\MyFile.JSON
$uncshare = "\\server\sharename"
So Your Rename-Item ... $_.FullName+$uncshare will try to rename it to:
C:\temp\FileRemediation\Scripts\..\temp\1st\MyFile.JSON\\server\sharename
Which is not a valid path.
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.
I need to get a TFS build process working where I dont control the agent, or any of the drop locations.
I have a post-build script which is shown Here...but I dont know how to edit it to so that it preserves the folder structure across multiple project directories.
I know I need to edit the file copy itself, but I am unsure of how to go about doing this in powershell...
foreach ($file in $files)
{
#Do something here to create the correct path on the drop server
Copy $file $Env:TF_BUILD_BINARIESDIRECTORY
}
The end goal is that instead of having all the build dlls in a single directory, I have them structured in folders per project.
Following is a simple script to achieve the feature you want, you can update it base on your folder structure and requirement.
Get-ChildItem -Path $Env:TF_BUILD_BUILDDIRECTORY -Filter *.*proj -Recurse | ForEach-Object {
$projectfolder = $_.BaseName
$currentpath = $_.Directory.ToString()
$copypath = $currentpath + '\obj'
$targetpath = $Env:TF_BUILD_BINARIESDIRECTORY + '\' + $projectfolder
Write-Host "Copying files from $copypath to $targetpath"
Copy-Item $copypath $targetpath -Recurse
}