Deleting cabinet files - powershell

I'm trying to create a script to delete cabinet files in virtual servers. For some reason, the code that I've created ends up not deleting any cabinet files and instead tries to delete the entire WINDOWS Directory, and I have no idea why this is occurring. Was curious if anyone might have any ideas on what the issue may be, since I can't find anything:
$dir = "\\$server" + '\C$\windows'
$cabinetArray = #()
foreach ($item in get-childitem -path $dir){
if ($item.name -like "*.cab"){
$cabinetArray = $cabinetArray + $item
}
}
for ($i = 0; $i -le $cabinetArray.length; $i++){
$removal = $dir + "\" + $cabinetArray[$i]
remove-item $removal -force -recurse
}
I did some testing and it seems that for some reason my array that I'm trying to use to gather all the cabinet files isn't even getting filled for some reason. I'm not sure if there's a specific way to only gather the .cab files since right now whenever I run this on my test server it tries deleting everything.

I don't know if deleting all the cab files in that folder is a good idea or not, but I'll answer your question. You're doing a lot of math and building your own collection of objects when PoweShell will do it all for you. Try something like this:
$dir = "\\" + $server + '\C$\windows'
$cabinetFiles = Get-ChildItem -Path $dir -Filter "*.cab" -Recurse
$cabinetFiles | %{
Remove-Item -Path $_.FullName -Force
}
Or, as a one liner:
Get-ChildItem -Path ("\\" + $server + '\C$\windows') -Filter "*.cab" -Recurse | %{Remove-Item -Path $_.FullName -Force}

Use the pipeline, here's a simplified version of your code (remove -WhatIf do delete the files). The code gets all *.cab files from the windows directory of the remote box (recursively), makes sure that only file objects passes on and then deletes them.
Get-ChildItem "\\$server\admin$" -Filter *.cab -Recurse |
Where-Object {!$_.PSIsContainer} |
Remove-Item -Force -WhatIf

For some reason, the code that I've created ends up not deleting any cabinet files and instead tries to delete the entire WINDOWS Directory, and I have no idea why this is occurring.
It is occurring because your for loop is being entered, and that is happening because $cabinetArray's length is zero. Once the for loop is entered, the $removal variable is assigned the value of $dir plus a trailing backslash. You are then calling remove-item on the windows directory.

Related

Is there a way to add a postfix to this script?

So I asked here before about helping with a script to copy and paste files from one folder to another.
However, after I was done, I found that some of the files went missing. I had 600,393 files but when I checked my new folder it only had 600,361 files.
I think it may have been overwritten by duplicates even though the naming convention was supposed to stop those kinds of problems.
Here's the script
$destfolder = '.\destfolder\'
Get-ChildItem -Recurse -File .\srcfolder\ |
Invoke-Parallel {
$_ | Copy-Item -Destination (
Join-Path $using:destfolder ($_.directory.parent.name, $_.directory.name, $_.name -join '-')
) -Verbose -WhatIf
}
(Thanks to the great dudes on r/software, r/Techsupport, and mklement0)
So is there a way to add a postfix that adds a 0 to the name of any file that has the same name as a file already in a folder?
like directory-subdirectory-0-filename.ext
EDIT- Problem is all the files are read-only not hidden, I don't want any hidden files.
Note that Get-ChildItem doesn't include hidden items by default, which may explain at least part of the the discrepancy.
Use the -Force switch to include hidden items.
Separately / additionally, you can deal with name collisions as follows:
$destfolder = '.\destfolder\'
Get-ChildItem -Force -Recurse -File .\srcfolder\ |
Invoke-Parallel {
$newName = Join-Path $using:destfolder ($_.directory.parent.name, $_.directory.name, $_.name -join '-')
# Try to create the file, but only if it doesn't already exist.
if ($null -eq (New-Item $newName -ErrorAction SilentlyContinue)) {
# File already exists -> create duplicate with GUID.
$newName += '-' + (New-Guid)
}
$_ | Copy-Item -Destination $newName -Verbose
}
Note:
With multi-threaded execution, assigning sequence numbers to duplicates would be a nontrivial undertaking, as each thread would have to "reserve" a sequence number and ensure that no other thread claims it before copying to a file incorporating this number is complete.
To avoid such challenges, the above approach simply appends a - plus a GUID to the target file name.

Powershell script which will search folders with regex and which will delete files older than XX

I need a powershell script ;
it must search some subfolders which folders names are starting with character between 1 and 6 (like 1xxxx or 2xxx)
and using the name of these folders as variable it must look under each folder for the *.XML files which are older than 30 min
and if it finds them it must delete it.
there may be more than one folder at same time, which are providing the same conditions so IMO using an array is a good choice. But I'm always open to other ideas.
Anybody can help me please ?
Basically I was using this before the need changes but now it doesnt help me.
powershell -nologo -command Get-ChildItem -Path C:\geniusopen\inbox\000\ready\processed | Where CreationTime -lt (Get-Date).AddDays(-10) | remove-item
Thank you
You can do something like the following and just remove -WhatIf if you are satisfied with the results:
$Time = (Get-Date).AddMinutes(-30)
Get-ChildItem -Path 'C:\MostCommonLeaf' -Recurse -File -Filter '*.xml' |
Where {$_.CreationTime -lt $Time -and (Split-Path $_.DirectoryName -Leaf) -match '^[1-6]' -and $_.Extension -eq '.xml'} |
Remove-Item -WhatIf
MostCommonLeaf would be the lowest level folder that could start as your root search node. We essentially don't want to traverse directories for nothing.
You could potentially make the script above better if you know more about your directory structure. For example, if it is predictable within the path where the 1xxx folders will be, you can construct the -Path parameter to use the [1-6] range wildcard. -Filter '*.xml' could also return .xmls files for example, so that's why there is additional extension condition in the Where.
Using -Recurse and -Include together generally results in much slower queries. So even if tempted, I would avoid a solution that uses those together.
If there are millions of files/directories, a different command construction could be better. Running Split-Path millions of times could be less efficient than just matching on the directory name, e.g. where {$_.DirectoryName -match '\\[1-6][^\\]*$'}.
I think you are looking for something like this:
$limit = (Get-Date).AddMinutes(-30)
$path = "C:\Users\you\xxx"
$Extension = "*.xml"
Get-ChildItem -Path $path -Filter $Extension -Force | Where-Object {$_.CreationTime -lt $limit} | Remove-Item
I haven't tested it though.
Keep in mind whether you need: $.CreationTime or $.LastWriteTime

PowerShell to copy files to destination's subfolders while excluding certain folders in the destination

So I have danced with this off and on throughout the day and the timeless phrase "There's more than one way to skin a cat" keeps coming to mind so I decided to take to the community.
Scenario:
Source folder "C:\Updates" has 100 files of various extensions. All need to be copied to the sub-folders only of "C:\Prod\" overwriting any duplicates that it may find.
The Caveats:
The sub-folder names (destinations) in "C:\Prod" are quite dynamic and change frequently.
A naming convention is used to determine which sub-folders in the destination need to be excluded when the source files are being copied (to retain the original versions). For ease of explanation lets say any folder names starting with "!stop" should be excluded from the copy process. (!stop* if wildcards considered)
So, here I am wanting the input of those greater than I to tackle this in PS if I'm lucky. I've tinkered with Copy-Item and xcopy today so I'm excited to hear other's input.
Thanks!
-Chris
Give this a shot:
Get-ChildItem -Path C:\Prod -Exclude !stop* -Directory `
| ForEach-Object { Copy-Item -Path C:\Updates\* -Destination $_ -Force }
This grabs each folder (the -Directory switch ensures we only grab folders) in C:\Prod that does not match the filter and pipes it to the ForEach-Object command where we are running the Copy-Item command to copy the files to the directory.
The -Directory switch is not available in every version of PowerShell; I do not know which version it was introduced in off the top of my head. If you have an older version of PowerShell that does not support -Directory then you can use this script:
Get-ChildItem -Path C:\Prod -Exclude !stop* `
| Where-Object { $_.PSIsContainer } `
| ForEach-Object { Copy-Item -Path C:\Updates\* -Destination $_ -Force }
To select only sub folders which do not begin with "!stop" do this
$Source = "C:\Updates\*"
$Dest = "C:\Prod"
$Stop = "^!stop"
$Destinations = GCI -Path $Dest |?{$_.PSIsContainer -and $_.Name -notmatch $Stop }
ForEach ($Destination in $Destinations) {
Copy-Item -Path $Source -Destination $Destination.FullName -Force
}
Edited Now copies all files from Update to subs of Source not beginning with "!stop" The -whatif switch shows what would happen, to arm the script remove the -whatif.
Edit2 Streamlined the script. If also Sub/sub-folders of C:\Prod shall receive copies include a -rec option to the gci just in front of he pipe.

Why is my PowerShell script writing blank lines to console?

I have a bit of an odd problem. Or maybe not so odd. I had to implement a "custom clean" for a PowerShell script developed for building some unique configurations for my current project (the whys are not particularly important). Basically it copies a bunch of files from the release directories into some temporary directories with this code:
$Paths = Get-ChildItem $ProjectPath -recurse |
Where-Object { ($_.PSIsContainer -eq $true) -and
(Test-Path($_.Fullname + 'bin\release')) } |
Select-Object Fullname
ForEach ($Path in $Paths)
{
$CopyPath = $Path.Fullname + '\bin\Temp'
$DeletePath = $Path.Fullname + '\bin\Release'
New-Item -ItemType directory -path $CopyPath
Copy-Item $DeletePath $CopyPath -recurse
Remove-Item $DeletePath Recurse
}
And after the build copies it back with:
ForEach ($Path in $Paths)
{
$CopiedPath = $Path.Fullname + '\bin\Temp\'
$DeletedPath = $Path.Fullname + '\bin\Release\'
$Files = Get-ChildItem $CopiedPath -recurse |
where-object {-not $_PSIsContainer}
ForEach ($File in $Files)
{
if(-not (Test-Path ($DeletedPath+$File.Name)))
{
Copy-Item $File.Fullname ($DeletedPath+$File.Name)
}
}
Remove-Item $CopyPath -recurse -force
}
This is pretty clunky and noobish (Sorry, I'm a PowerShell noob drinking from a fire hose), but it works for the purpose and I will clean it up later. However, when it executes the initial copy to the temp directories, it writes a lot of blank lines to the screen, which isn't ideal as I have a message I display while this process is executing to assure our CM doesn't freak out and think it broke, but this message is blown away by the blank lines. Do you know what might be causing this and how I might solve this? I'm using PowerShell 2.0 out of the box and due to the nature of this project I can't upgrade or get any outside libraries. Thanks guys.
If the only thing you're looking to do is clean up the console output, then all you need to do is use the pipeline. You can start the command with [void], which will exclude all information from the pipeline. You can also pipe the whole thing into the Out-Null cmdlet, which will trap all output, except for the lines that don't have output.
The New-Item cmdlet by default returns output to the console on my version of Windows PowerShell (4.0). This may not be true on previous versions, but I think it is... Remove-Item also doesn't return any output, usually. If I were to take a stab, I'd kill output on those lines that use the "Item" noun using one of the methods mentioned above.

How to keep a specific folder and delete rest of the files using powershell

I am trying delete all files within a folder but there is 1 folder called pictures which I would like to keep but don't know how to do that. I am using the following script , it deletes everything in a folder
if ($message -eq 'y')
{
get-childitem "C:\test" -recurse | % {
remove-item $_.FullName -recurse
}
}
One solution is to use something like:
Get-ChildItem -Path "c:\test" -Recurse | Where-Object { $_.FullName -cnotmatch "\\Pictures($|\\)" -and (Get-ChildItem $_.FullName -Include "Pictures" -Recurse).Length -eq 0 } | Remove-Item -Recurse -ErrorAction SilentlyContinue;
I suspect there must be a way more elegant way to do this. Here's what this does: it enumerates all files in the C:\test folder recursively (Get-ChildItem), then it removes all items from the result list using Where-Object where the path contains the directory to be excluded (specified using regex syntax) or when the item in question has child items that contains the file or directory to be excluded. The resulting list is fed to Remove-Item for removal. The -ErrorAction SilentlyContinue switch is applied to prevent errors being logged with recursive removal.
Get-ChildItem $PSScriptRoot -Force| Where-Object {$_.Name -ne "Pictures"} | Remove-Item -Recurse
I just tried this, and it worked for me. If you want to change what is deleted just change the "Pictures". This uses $PSScriptRoot for the path, which is the execution path of the Powershell script. You can rename that to be the path of where you want to delete.