The script I am including below needs to accomplish the following tasks. It needs to get a list of servers from AD, then iterate through each of those server names and grab the second to the newest folder in a directory, rename it, and copy it to another server.
The Copy-Item command is not working when I have it in the foreach loop, as written below:
#gathering server names
$serverList = (Get-ADComputer -Filter "Name -like 'Q0*00*'" -SearchBase "OU=MPOS,OU=Prod,OU=POS,DC=N,DC=NET").name | Sort-Object | Out-File C:\Temp\MPOS\MPOSServers.txt
$serverListPath = "C:\Temp\MPOS\MPOSServers.txt"
#Retrieve a list of MPOS Print servers from text file and set to $serverNames
$serverNames = Get-Content -Path $serverListPath
#Iterate through each of the server names
foreach ($serverName in $serverNames) {
$reportServer = "a03"
Get-ChildItem "\\$($serverName)\d$\MPosLogs\Device" |
Where { $_.PSIsContainer } |
Sort CreationTime -Descending |
Select -Skip 1 |
Select -First 1 |
ForEach-Object {
Rename-Item -Path $_.FullName -NewName ("$serverName" + "_" + $_.Name) -PassThru |
Copy-Item -Destination "\\$($serverName)\c$\temp\MPOS\Logs"
}
}
However, it works fine if I am testing it outside of the ForEach loop, as written below:
Get-ChildItem "\\$($serverName)\d$\MPosLogs\Device" |
Where { $_.PSIsContainer } |
Sort CreationTime -Descending |
Select -Skip 1 |
Select -First 1 |
ForEach-Object {
Rename-Item -Path $_.FullName -NewName ("$serverName" + "_" + $_.Name) -PassThru |
Copy-Item -Destination "\\$($serverName)\c$\temp\MPOS\Logs"
}
Any ideas as to why it is not working in the full script? I am not changing anything when I test it, I am just running the above commands without being in the ForEach loop. It is completing the rest of the tasks, except for the folder copies. The folder copy only works if I am testing it outside of the ForEach loop on a single server.
When I say "it doesn't work", there are no errors or anything like that. It simply is not copying the folders.
Thank you! :)
LG
#MikeGaruccio well that is extremely embarrassing. I think I have just been staring at this script for too long, and did not realize that I was not actually copying the folders to $reportServer - it's a good thing you asked!!! It definitely matters. All is well now, after changing the final $serverName to actually read $reportServer. Thank you, and sorry for wasting your time...I appreciate your help a lot.
Related
I got a script to take backup and to remove files in generations (group). I need to add some logging of which files it copies and also which ones it deletes. In all my previous scrips, I been using Out-File, but in this case for the copy I can't get it to work.
If I add it to the Copy-Item part it creates the file but it simply wont write any input. What I am missing?
#$a = Get-Date
#$a.ToUniversalTime()
foreach ($file in (Get-ChildItem -File $localpath -Recurse | Where {$_.LastWriteTime -gt (Get-Date).AddDays(-1)})) {
Copy-Item -Path $file.FullName -Destination "C:\qlikview Storage\privatedata\backup\$file.$(get-date -f yyyy-MM-dd)"
}
$Groups = Get-ChildItem -Path "C:\qlikview Storage\privatedata\backup" |
Group-Object -Property Basename |
Where-Object {$_.Count -gt 2}
foreach ($g in $Groups) {
$g.Group |
sort LastWriteTime -Descending |
select -Skip 2 |
foreach {del $_.FullName -Force}
}
The #a is for later to add timestamps the logging to see how long it takes.
Am I thinking wrong assuming Out-File is the way to go?
Add the -Verbose switch to your Copy-Item and Remove-Item commands. This will dump the copied/removed files to the verbose stream.
Afterwards you can redirect the verbose stream to the output stream (4>&1) and log it the a file.
Example :
Copy-Item... -Verbose 4>&1 | Out-file log.txt
Additional info can be found in about_Redirection.
I have a folder structure with, for example, 100 folders. Each folder has 200 files in it.
I would like to delete (via scheduled task) all files in each folder but keep the last 10 versions of it.
I am trying to upskill in Powershell so I am guessing that this should be pretty simple. I have created this script,
#Delete all files, keep last 10 versions#
$Directory = "D:\Octopus\Packages"
$Keep = "10"
Get-ChildItem $Directory| ?{ $_.PSIsContainer } | Select-Object FullName | Export-Csv $Directory\FolderList.csv
$FolderList = import-csv $Directory\FolderList.csv
ForEach ($row in $FolderList)
{
Get-ChildItem -Recurse | where{-not $_.PsIsContainer}| sort CreationTime -desc | select -Skip $Keep | Remove-Item -Force
}
It appears to be looping through each folder, but keeping the last 10 files for the entire folder structure, not per folder. So some folders have 0 files, some may have 2 files, some may have 8 files.
Any pointers would be appreciated
Thanks !
If you actually need to have that CSV then just modify Get-ChildItem -Recurse to Get-ChildItem $row -recurse. However, if you don't need to be creating the CSV, you can remove of that and just pipe the results of your first Get-ChildItem into the next action.
$Directory = "D:\Octopus\Packages"
$Keep = "10"
Get-ChildItem $Directory| ?{ $_.PSIsContainer } | Select-Object FullName |
ForEach-object {Get-ChildItem $_.fullname -Recurse |
where{-not $_.PsIsContainer}| sort CreationTime -desc |
select -Skip $Keep | Remove-Item -Force }
I'm trying to run the script below in multiple paths using an array. My goal is to delete folders keeping the last 7 versions, but it is not working as expected. The action is only taking into account the first path D:\Test1.
I believe that I should add something like ($folders in $folders) after ForEach-Object but I don know how.
Any idea what I missing here?
$path = #("D:\Test1","D:\Test2","D:\Test3")
$folders = Get-ChildItem -Path $path -Recurse |
Where-Object { $_.PSIsContainer } |
Group-Object { $_.Name.Split('_')[0] } |
ForEach-Object $Folders {
$_.Group |
sort CreationTime -Descending |
Select -Skip 7 |
foreach { Remove-Item $_.FullName -Force -WhatIf }
}
This should do your job.
$path= #("D:\Test1","D:\Test2","D:\Test3")
$folders= Get-ChildItem -path $path -Recurse | Where-Object {$_.PsIsContainer} |Group-Object {$_.FullName.Split('_')[0] }
ForEach($folder in $folders)
{
$folder.Group | sort CreationTime -Descending | Select -Skip 7|% { Remove-Item $_.fullname -Force -whatIf}
}
I tested in my local and it is working fine. Although I didn't get any error in your code except few formatting issue which I have taken into variable and sorted it out cause I got tangled in too many pipeline objects.
If you are using foreach after a pipeline , that means it will take the pipeline objects one by one only. But if you are separately using it , then you have to assign each iteration into a variable.
Hope it helps you.
I got the answer from #Robert Israelsson !
" If you change your group-object to not group by name but instead fullname you will get the desired result."
From:
$folders= Get-ChildItem -path $path -Recurse | Where-Object {$_.PsIsContainer} |Group-Object {$_.Name.Split('_')[0] }
To:
$folders= Get-ChildItem -path $path -Recurse | Where-Object {$_.PsIsContainer} |Group-Object {$_.FullName.Split('_')[0] }
And this works perfectly!
I am trying to write a script that gathers a list of server names, then iterates through each server name and grabs the second to the newest folder in a directory, renames it, and copies it to another server. I am running into trouble with getting this to work all in one command, and I am not sure what is the best way to split it up and have it work properly. I get errors when trying different things, the main one is "rename-item: cannot evaluate parameter "NewName" because its argument is specified as a script block and there is no input". Here is the code snippet that causes this error:
$serverNames = Get-Content -Path "C:\servers.txt"
foreach ($serverName in $ServerNames) {
$reportServer = "a56741035"
Get-ChildItem "\\$($serverName)\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | Rename-Item -NewName { "$serverName" + "_" + $_.Name } | Copy-Item $_.FullName -Destination "\\$($reportServer)\c$\temp\mpos\logs" }
Another is "cannot bind argument to parameter 'Path' because it is null". Here is the code that causes this error:
$serverNames = Get-Content -Path "C:\servers.txt"
foreach ($serverName in $ServerNames) {
$reportServer = "a56741035"
Get-ChildItem "\\$($serverName)\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -NewName { "$serverName" + "_" + $_.Name } | Copy-Item $_.FullName -Destination "\\$($reportServer)\c$\temp\mpos\logs" } }
I feel like I am very close, and just don't see something. Any assistance is greatly appreciated, thank you so much. Have a great day.
EDITED TO INCLUDE NEWEST CODE:
Get-ChildItem "\\$($serverName)\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName ( "$serverName" + "_" + $_.Name ) ; Copy-Item $_.FullName -Destination "\\$($reportServer)\c$\temp\mpos\logs" }
CODE FOR NEWEST COMMENTS:
Get-Child-Item "\\$serverName\d$\mposlogs\device" | where {$_.PSIsContainer} | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName ( "$serverName" + "" + $.Name ) ; Copy-Item ( "$serverName" + "" + $.Name ) -Destination "\\$reportServer\c$\temp\mpos\logs" }
So this turned out to be more difficult than I thought it would be, the trick being that Rename-Item seems to basically consume the pipelined object without giving you a chance to use that object in the subsequent name(using a scriptblock is also not really ideal, much easier to use a simple concatted string) so you need to change the Rename-Item portion of your loop slightly to
Rename-Item -Path $_.FullName -NewName ("$serverName" + "_" + $_.Name)
As far as the syntax changes prageeth suggests you can make them but they are unneeded and imo increase ambiguity(Get-Childitem does not require an escape character to access an admin share and there is nothing wrong with using $($) syntax even when not accessing a property).
EDIT
Ok after working in the comments for a bit here is an updated version of the full GCI pipeline. I tested it successfully in my environment so fingers crossed it works for you. Please let me know if any part of it doesn't make sense.
Get-ChildItem "\\$serverName\d$\mposlogs\device" | where {$_.PSIsContainer} | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName ( "$serverName" + "_" + $_.Name ) -PassThru | Copy-Item -Destination "\\$reportServer\c$\temp\mpos\logs" }
In situation like this is always best to analyse the command output first...
# Rename doesn't output anything by default
Rename-Item ToBeRenamed.txt Renamed.txt
# For most commands with that default behaviour, you can use -PassThru to force output
Rename-Item ToBeRenamed.txt Renamed.txt -PassThru
# Once we have something in the pipeline, we can pipe result to Copy-Item
Rename-Item ToBeRenamed.txt Renamed.txt -PassThru | Copy-Item -Destination C:\temp
ls C:\temp\Renamed.txt
I would recommend avoiding ForEach-Object here - it's not really needed. You just need to control output from commands, that's it.
Edit:
As user mike mentioned, it appears that admin shares doesn't need $ to be escaped with. The real problem is rename-Item cmdlet missing the -Path parameter. Thanks for bringing that up Mike!
However I still recommend removing the extra $ over $($serverName) to reduce ambiguity.
Try this!
Get-ChildItem "\\$serverName\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName { "$serverName" + "_" + $_.Name } | Copy-Item $_.FullName -Destination "\\$reportServer\c$\temp\mpos\logs" } }
I have this PowerShell code that compares 2 directories and removes files if the files no longer exist in the source directory.
For example say I have Folder 1 & Folder 2. I want to compare Folder 1 with Folder 2, If a file doesn't exist anymore in Folder 1 it will remove it from Folder 2.
this code works ok but I have a problem where it also picks up file differences on the date/time. I only want it to pick up a difference if the file doesn't exist anymore in Folder 1.
Compare-Object $source $destination -Property Name -PassThru | Where-Object {$_.SideIndicator -eq "=>"} | % {
if(-not $_.FullName.PSIsContainer) {
UPDATE-LOG "File: $($_.FullName) has been removed from source"
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue
}
}
Is there an extra Where-Object {$file1 <> $file2} or something like that.?
I am not sure how you are getting the information for $source and $destination I am assuming you are using Get-ChildItem
What i would do to eliminate the issue with date/time would be to not capture it in these variables. For Example:
$source = Get-ChildItem C:\temp\Folder1 -Recurse | select -ExpandProperty FullName
$destination = Get-ChildItem C:\temp\Folder2 -Recurse | select -ExpandProperty FullName
By doing this you only get the FullName Property for each object that is a child item not the date/time.
You would need to change some of the script after doing this for it to still work.
If I am not getting it wrong, the issue is your code is deleting the file with different time-stamp as compared to source:
Did you try -ExcludeProperty?
$source = Get-ChildItem "E:\New folder" -Recurse | select -ExcludeProperty Date
The following script can serve your purpose
$Item1=Get-ChildItem 'SourcePath'
$Item2=Get-ChildItem 'DestinationPath'
$DifferenceItem=Compare-Object $Item1 $Item2
$ItemToBeDeleted=$DifferenceItem | where {$_.SideIndicator -eq "=>" }
foreach ($item in $ItemToBeDeleted)
{
$FullPath=$item.InputObject.FullName
Remove-Item $FullPath -Force
}
Try something like this
In PowerShell V5:
$yourdir1="c:\temp"
$yourdir2="c:\temp2"
$filesnamedir1=(gci $yourdir1 -file).Name
gci $yourdir2 -file | where Name -notin $filesnamedir1| remove-item
In old PowerShell:
$yourdir1="c:\temp"
$yourdir2="c:\temp2"
$filesnamedir1=(gci $yourdir1 | where {$_.psiscontainer -eq $false}).Name
gci $yourdir2 | where {$_.psiscontainer -eq $false -and $_.Name -notin $filesnamedir1} | remove-item
If you want to compare files in multiple dir, use the -recurse option for every gci command.