foreach copy-item not working - powershell

My Copy-Item doesn't work when included in a foreach loop.
Much like Powershell: Copy-Item not working when in ForEach loop
Only my destination folder is not set the same as the originating folder which seemed to be the problem there.
This is the very basic function. My objective is to grab the latest log files from a directory containting log files for lots of stuff. I'm only interested in a few defined in $servers. A line in Servers.txt looks like this: \\clientname\d$\logdirectory\processlog\
When I Set-Location to a path in servers.txt and run Get-ChildItem it works as expected.
I also need to generate a new folder for each object in \Logs\ but one thing at a time.
$servers = #()
$servers = Get-Content c:\Test\Servers.txt
$destServer = #()
$destServer = ( "clientname")
$destinationFolder = "\\" + $destServer + "\d$\Logs\"
foreach ($serverpath in $servers) {
Write-Host " Copying from $serverpath "
Set-Location -literalpath $serverpath |
Get-ChildItem |
Sort-Object -Descending LastWriteTime |
Select -First 2 |
Copy-Item -Destination $destinationFolder -Recurse -Force
}

Related

PowerShell works only when running in a console and not as a PS1 executable file

I am fairly new to PowerShell and am having challenges trying to get a PS1 executable file to work. Running the script in a PowerShell console works completely fine and copy's items and creates the correct log filename.
The expectation would be to Right-click the PS1 file containing the script, run the script with "Run with PowerShell", and then allow the script to finish with a log file populated and files copied when user prompt selects yes.
At this point, there are no errors messages, other than the PS1 file script gets replaced by ton of unrecognizable symbols/characters and creates the log file as "Box Project Files.ps1JobFileLocations.log" instead of "JobFileLocations.log".
The PowerShell version being used is 5.1. Windows 10 OS. Set-ExecutionPolicy was set to Unrestricted and confirmed as Unrestricted for CurrentUser and LocalMachine. Unblock-File was also tried.
Below is the script that works in a PowerShell Console but not as a PS1 executable file.
# Drawing Tag Searches
$MechDWGFilterList = #('*IFC*','*mech*', '*permit*', '*final*')
$DatabaseFilterList = #('*field*','*software*')
# Root folder and destination folder
$JobNumber = '*'+(Read-Host -Prompt 'Enter in job number')+'*'
$srcRoot = 'C:\Users\username\Box\'
$JobRoot = (Get-ChildItem -Path $srcRoot -Filter "*Active Projects*" -Recurse -Directory -Depth 1).Fullname
$dstRoot = $MyInvocation.MyCommand.Path
# Find job numer pdf file
$JobFolder = (Get-ChildItem -Path $JobRoot -Filter "$JobNumber" -Recurse -Directory -Depth 0).Fullname
$Logfile = $dstRoot+"JobFileLocations.log"
$reply = Read-Host -Prompt "Make a copy of relevant project files to local drive?[y/n]"
# Find sub-folder from job folder
$ProposalFolder = (Get-ChildItem -Path $JobFolder -Filter "*Proposals*" -Recurse -Directory).Fullname
$MechDWGFolder = (Get-ChildItem -Path $JobFolder -Filter "*Plans*" -Recurse -Directory).Fullname
$SubmittalFolder = (Get-ChildItem -Path $JobFolder -Filter "*Submittal*" -Recurse -Directory).Fullname
$DatabaseFolder = (Get-ChildItem -Path $JobFolder -Filter "*Backup*" -Recurse -Directory).Fullname
$EstimateFolder = (Get-ChildItem -Path $JobFolder -Filter "*Estimate*" -Recurse -Directory).Fullname
# Find files from list
$ProposalList = Get-ChildItem -Path $ProposalFolder -Filter '*proposal*.pdf' -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$MechDWGList = Get-ChildItem -Path $MechDWGFolder -Filter *.pdf -r | Sort-Object -Descending -Property LastWriteTime
$SubmittalList = Get-ChildItem $SubmittalFolder -Filter '*submittal*.pdf' -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$DatabaseList = Get-ChildItem $DatabaseFolder -Filter *.zip -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$EstimateList = Get-ChildItem -Path $EstimateFolder -Filter *.xl* -r | Sort-Object -Descending -Property LastWriteTime
# Log file path location and copy file to local directory
# Function to add items to a log text file
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
# Log file path location and copy file to local directory
LogWrite "::==========================================::`n|| Project Document Paths ||`n::==========================================::"
LogWrite "`nNote: If a section has more than one file path, files are listed from most recent to oldest.`n"
LogWrite "----------Scope Document/Proposal(s)----------"
foreach ($file in $ProposalList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n-------------Mechanical Drawing(s)------------"
foreach ($file in $MechDWGList)
{
# Where the file name contains one of these filters
foreach($filter in $MechDWGFilterList)
{
if($file.Name -like $filter)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
}
}
LogWrite "`n-------------Controls Submittal(s)------------"
foreach ($file in $SubmittalList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n-------------------Database-------------------"
foreach ($file in $DatabaseList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n------------------Estimate(s)-----------------"
foreach ($file in $EstimateList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost")
{
Write-Host "Press any key to continue..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
Could someone help me understand what is wrong with running the script as a PS1 file?
The problem with your script is this particular line:
$dstRoot = $MyInvocation.MyCommand.Path
$MyInvocation.MyCommand.Path resolves the rooted filesystem path to the script itself - which is why you get Box Project Files.ps1 (presumably the name of the script) in the log path.
The get the path of the parent directory of any file path, you can use Split-Path -Parent:
$dstRoot = Split-Path -LiteralPath $MyInvocation.MyCommand.Path -Parent
That being said, since Windows PowerShell 3.0, both the directory and script file paths have been available via the $PSCommandPath and $PSScriptRoot automatic variables, so you can simplify the code to just:
$dstRoot = $PSScriptRoot

Copy multiple sub-folders to one folder with PowerShell

I am new with PowerShell and I have small issue.
I have 2 folders on C:/ -- C:/folder1 and C:/folder2
folder1 contains 10 sub-folders, and I wants to copy only 3 of them to folder2.
I should do it with Remote Computer, so I should use Invoke-Command also.
Unfortunately, the program isn't works with my code.
$SubFolders = "C:/subfolder1", "C:/subfolder2", "C:/subfolder3"
$copy = InvokeCommand -ComputerName $compname | For-EachObject { Write-Host = "$SubFolders" }
$paste = "C:/folder2"
Copy-Item $copy $paste -Recurse -Force
Try this:
Invoke-Command -ComputerName $compname -ScriptBlock {param($folders,$paste) Copy-Item -Path $folders -Destination $paste} -ArgumentList $folders, $paste
Not the most elegant solution but it works.
To Move or Copy Files in Multiple Sub-Folders to a Single Folder.
Open a Command Prompt window. Use the following command-line example.
cd /d "d:\snaps\2016"
for /r %d in (*) do copy "%d" "d:\My pictures"`
This recursively copies all files in the “snaps\2016” folder to the “My pictures” folder.
Or try this:
$Path = 'S:\powerrocket\0test' #Root path to look for files
$DestinationPath = "C:\powerrocket\0test\"
#Grab a recursive list of all subfolders
$SubFolders = dir $Path -Recurse | Where-Object {$_.PSIsContainer} | ForEach-Object -Process {$_.FullName}
#Iterate through the list of subfolders and grab the first file in each
ForEach ($Folder in $SubFolders) {
$FullFileName = dir $Folder | Where-Object {!$_.PSIsContainer} | Sort-Object {$_.LastWriteTime} -Descending | Select-Object -First 1
Copy-Item -Path $FullFileName -Destination $DestinationPath -Force
}

Move amount of folders recursive to another folder in the same folder with PowerShell

I have a root folder (a mapped network drive) to Z, in this folder I have a folder named Archive and I would like to move some folders in Z to archive folder.
The titles of folders to move I have in a csv file.
I've created a PowerShell script, but somehow it does not really work, it does move one folder, but then nothing happens even in the PowerShell command, just empty and after a while nothing happens and I have to close the PowerShell window.
So if I have ten folders to copy only the first is moved and that is it.
Here is the code:
$currentPath = Split-Path -Parent $PSCommandPath;
$areaCsvPath = $currentPath + "\CSVFile.csv";
write-host $areaCsvPath;
$csv = Import-Csv $areaCsvPath;
$count =0;
$Creds = Get-Credential
foreach ($row in $csv)
{
Get-ChildItem -Path "Z:\" -Recurse |
Where-Object {$_.name -eq $row.Title} |
Move-Item -destination "Z:\_Archive" -Credential $Creds
$count++;
write-host $count;
}
CSV is as follows
Title
12345
22223
75687
...
I don't see why only one folder gets moved but you could try the following script which should be much faster because the Get-ChildItem cmdlet is only called once:
$currentPath = Split-Path -Parent $PSCommandPath;
$areaCsvPath = $currentPath + "\CSVFile.csv";
write-host $areaCsvPath;
$csv = Import-Csv $areaCsvPath;
$Creds = Get-Credential
Get-ChildItem -Path "Z:\" -Recurse |
Where-Object Name -in ($csv | select -expand Title) |
Move-Item -destination "Z:\_Archive" -Credential $Creds
If the folders are at the top level on Z: you should omit the -Recurse parameter. Also if you only want to move folders, you could add the -Directory switch to the Get-ChildItem invoke to further improve the performance.

powershell - copy specific user folders based on last modified

I need to copy the Documents, Favorites, and Desktop folders for each user that has logged in to the PC in the last 30 days.
I'm just starting to learn powershell and I've got a decent start I think, but I'm wasting too much time. This is a project for work, and I've found myself just digging for solutions to X problem only to run into another at the next turn. Spent about a month trying to get this sorted out thus far, and thrown away a lot of codes.
What I have as of right now is this:
Get-ChildItem -path c:\users |Where-Object { $_.lastwritetime -gt (get-date).AddDays(-30)}
I know that this line will return the user folders that I need. At this point, I need code that will go in to each childitem from above and pull out the Documents, Favorites, and Desktop folder.
Now the tricky part. I need the code to create a folder on c: with the username it is pulling those folders from.
So the solution should:
for each user logged in in last 30 days;
copy Documents, Favorites, Desktop folder from their user drive
create a folder on c:\ for that user name
paste Documents, Favorites, Desktop to that folder
To better cover the scope:
I have to reimage PCs a lot in my department. The process of "inventorying" a PC is copying those folders and replacing them on the new PC I image for the user. That way their desktop etc looks the same and functions the same when they get their new PC. This code will be part of a larger code that ultimately "inventories" the entire PC for me... Ultimately, I want to be able to run my script for 2 seconds and then pull X folders and X documents off the c: drive on that PC as opposed to click, click, click, click a hundred times for 9 users that have used the PC in the last 30 days.
Any ideas?
2dubs
$usersFoldr = Get-ChildItem -path c:\users | Where-Object { $_.lastwritetime -gt (get-date).AddDays(-30)}
foreach ($f in $usersFoldr){
$toFld = "c:usrTest\" + $f.Name +"\Desktop\"
New-Item $toFld -type directory -force
Get-ChildItem ($f.FullName + "\Desktop") | Copy-Item -destination $toFld -Recurse -Force
}
Thanks to #bagger for his contribution. He was close.
After some experimentation, I found that this is the actual solution:
$usersFoldr = Get-ChildItem -path c:\users | Where-Object {
$_.lastwritetime -gt (get-date).AddDays(-30)}
foreach ($f in $usersFoldr)
{
$doc = "c:\users\$f\documents"
$toFldDoc = "c:\$f\documents"
New-Item $doc -type directory -force
Copy-Item $doc $toFldDoc -recurse -Force
}
foreach ($f in $usersFoldr){
$desk = "c:\users\$f\desktop"
$toFldDesk = "c:\$f\desktop"
New-Item $desk -type directory -force
Copy-Item $desk $toFldDesk -recurse -Force
}
foreach ($f in $usersFoldr){
$fav = "c:\users\$f\favorites"
$toFldFav = "c:\$f\favorites"
New-Item $fav -type directory -force
Copy-Item $fav $toFldFav -recurse -Force
}
Then save this file, send a shortcut of it to the desktop, then change the target of the shortcut to this:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -f "C:\YOURDIRECTORY\YOURSCRIPTNAME.ps1"
Then run that shortcut as an administrator. Works like gold.
Thanks for your help, guys! :)
For anyone interested in the whole script:
Inventory script to copy pertinent files for all users in last 30 days, gather printer hostname/driver/IP, gather serialnumber, gather make/model.
$usersFoldr = Get-ChildItem -path c:\users | Where-Object {
$_.lastwritetime -gt (get-date).AddDays(-30)}
foreach ($f in $usersFoldr){
$doc = "c:\users\$f\documents"
$toFldDoc = "c:\inventory\$f\documents"
New-Item $doc -type directory -force
Copy-Item $doc $toFldDoc -recurse -Force
}
foreach ($f in $usersFoldr){
$desk = "c:\users\$f\desktop"
$toFldDesk = "c:\inventory\$f\desktop"
New-Item $desk -type directory -force
Copy-Item $desk $toFldDesk -recurse -Force
}
foreach ($f in $usersFoldr){
$fav = "c:\users\$f\favorites"
$toFldFav = "c:\inventory\$f\favorites"
New-Item $fav -type directory -force
Copy-Item $fav $toFldFav -recurse -Force
}
Get-WMIObject -class Win32_Printer | Select Name,DriverName,PortName
|Export-CSV -path 'C:\Inventory\printers.csv'
Get-WmiObject win32_bios |foreach-object {$_.serialnumber} |out-file
'c:\Inventory\SerialNumber.txt'
Get-WmiObject Win32_ComputerSystem | Select Model,Manufacturer |out-file
'c:\Inventory\MakeModel.txt'
Again, save this file, send a shortcut of it to the desktop, then change the target of the shortcut to this:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -f "C:\YOURDIRECTORY\YOURSCRIPTNAME.ps1"
You can also retrieve a list of installed software by adding this line to the script:
get-wmiobject win32_product | select Name |export-csv -path 'c:\inventory
\software.csv'

Appending outputs to existing text file

I have this piece of powershell code below which creates an individual text file in the folder C:\Users\XX\Desktop\info\ from each individual zip file in the folder C:\Users\XX\Desktop\Powershell\Zip, with the name of the text files being the name of the zip files.
Get-ChildItem -Path "C:\Users\XX\Desktop\Powershell\Zip" -Recurse -exclude '*.info' | ForEach { [System.IO.File]::WriteAllText("C:\Users\XX\Desktop\info\"+ $_.Name + ".txt", $_.FullName)}
ontop of that I have the script below which gets the last modified date for the zip files
$path = 'C:\Users\XX\Desktop\Powershell\Zip'
$files = Get-ChildItem $path -Recurse -excluse '*.info'
foreach($file in $files){
$file.lastwritetime
and also this command that gets the computer name
{
(Get-WmiObject Win32_Computersystem).name
}
All these will be in one script, but I need the outputs of the 2nd and 3rd section of the script to append to the text file created in the first section of the script, appending to the appropriate file.
I have tried a couple of commands, the main one being [System.IO.File]::AppendAllText, but I cant seem to get anywhere with this.
Any ideas on the right way I should be doing this?
Thankyou.
You can try this :
$path = 'C:\Users\XX\Desktop\Powershell\Zip'
$files = Get-ChildItem $path -Recurse -Exclude '*.info'
$ComputerName = (Get-WmiObject Win32_Computersystem).name
foreach($file in $files) {
$OutputFilePath = "C:\Users\XX\Desktop\info\"+ $file.Name + ".txt"
[System.IO.File]::WriteAllText($OutputFilePath, $file.FullName)
$file.lastwritetime | Add-Content $OutputFilePath
$ComputerName | Add-Content $OutputFilePath
}