I'm trying to delete all folders in \\kiewitplaza\vdi\Appsense_profiles that end with .old. The piece I have that says Write-Host $name is just me trying to test before I delete anything.
$name = Get-ChildItem "\\kiewitplaza\vdi\Appsense_profiles"
if ($name.name.EndsWith(".old")) {
Write-Host $name
#Remove-Item "\\kiewitplaza\vdi\Appsense_profiles\$name.old"
}
Get-ChildItem produces a list of objects. Use a pipeline for processing that list:
Get-ChildItem '\\kiewitplaza\vdi\Appsense_profiles' |
Where-Object { $_.Name -like '*.old' } |
Remove-Item
Related
In a directory, there are files with the following filenames:
ExampleFile.mp3
ExampleFile_pn.mp3
ExampleFile2.mp3
ExampleFile2_pn.mp3
ExampleFile3.mp3
I want to iterate through the directory, and IF there is a filename that contains the string '_pn.mp3', I want to test if there is a similarly named file without the '_pn.mp3' in the same directory. If that file exists, I want to remove it.
In the above example, I'd want to remove:
ExampleFile.mp3
ExampleFile2.mp3
and I'd want to keep ExampleFile3.mp3
Here's what I have so far:
$pattern = "_pn.mp3"
$files = Get-ChildItem -Path '$path' | Where-Object {! $_.PSIsContainer}
Foreach ($file in $files) {
If($file.Name -match $pattern){
# filename with _pn.mp3 exists
Write-Host $file.Name
# search in the current directory for the same filename without _pn
<# If(Test-Path $currentdir $filename without _pn.mp3) {
Remove-Item -Force}
#>
}
enter code here
You could use Group-Object to group all files by their BaseName (with the pattern removed), and then loop over the groups where there are more than one file. The result of grouping the files and filtering by count would look like this:
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1
Count Name Group
----- ---- -----
2 ExampleFile {ExampleFile.mp3, ExampleFile_pn.mp3}
2 ExampleFile2 {ExampleFile2.mp3, ExampleFile2_pn.mp3}
Then if we loop over these groups we can search for the files that do not end with the $pattern:
#'
ExampleFile.mp3
ExampleFile_pn.mp3
ExampleFile2.mp3
ExampleFile2_pn.mp3
ExampleFile3.mp3
'# -split '\r?\n' -as [System.IO.FileInfo[]] | Set-Variable files
$pattern = "_pn"
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1 | ForEach-Object {
$_.Group.Where({-not $_.BaseName.Endswith($pattern)})
}
This is how your code would look like, remove the -WhatIf switch if you consider the code is doing what you wanted.
$pattern = "_pn.mp3"
$files = Get-ChildItem -Path -Filter *.mp3 -File
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1 | ForEach-Object {
$toRemove = $_.Group.Where({-not $_.BaseName.Endswith($pattern)})
Remove-Item $toRemove -WhatIf
}
I think you can get by here by adding file names into a hash map as you go. If you encounter a file with the ending you are interested in, check if a similar file name was added. If so, remove both the file and the similar match.
$ending = "_pn.mp3"
$files = Get-ChildItem -Path $path -File | Where-Object { ! $_.PSIsContainer }
$hash = #{}
Foreach ($file in $files) {
# Check if file has an ending we are interested in
If ($file.Name.EndsWith($ending)) {
$similar = $file.Name.Split($ending)[0] + ".mp3"
# Check if we have seen the similar file in the hashmap
If ($hash.Contains($similar)) {
Write-Host $file.Name
Write-Host $similar
Remove-Item -Force $file
Remove-Item -Force $hash[$similar]
# Remove similar from hashmap as it is removed and no longer of interest
$hash.Remove($similar)
}
}
else {
# Add entry for file name and reference to the file
$hash.Add($file.Name, $file)
}
}
Just get a list of the files with the _pn then process against the rest.
$pattern = "*_pn.mp3"
$files = Get-ChildItem -Path "$path" -File -filter "$pattern"
Foreach ($file in $files) {
$TestFN = $file.name -replace("_pn","")
If (Test-Path -Path $(Join-Path -Path $Path -ChildPath $TestFN)) {
$file | Remove-Item -force
}
} #End Foreach
I want to check duplicate file.If the condition of the file like this, it means duplicate. The same name but different extension.
AAA18WWQ6BT602.PRO
AAA18WWQ6BT602.XML
I can figure out this case with my script. But I have problem if I have this more than 1 .XML file like this
AAA18WWQ6BT602.PRO
AAA18WWQ6BT602.XML
AAA18WWQ6BT601.XML
AAA18WWQ6BT604.XML
This case, it will not detect that file AAA18WWQ6BT602.PRO and AAA18WWQ6BT602.XML duplicated.
Anyone can help me please.
Thanks
$duplicate = #()
#(Get-ChildItem "$Flag_Path\*.xml") | ForEach-Object { $duplicate += $_.basename }
if(Test-Path -Path "$Flag_Path\*$duplicate*" -Exclude *.xml)
{
Get-ChildItem -Path "$Flag_Path\*$duplicate*" -Include *.xml | Out-File $Flag_Path\Flag_Duplicate
Write-Host "Flag duplicated, continue for Error_Monitoring"
pause
Error_Monitoring
}
else{
Write-Host "Flag does not duplicate, continue the process"
}
The -Include parameter only works if the path on Get-ChildItem ends in \* OR if the -Recurse switch is used.
The following should do what you want:
$flagFolder = 'D:\*'
$dupeReport = 'D:\Flag_Duplicate.txt'
$duplicates = Get-ChildItem -Path $flagFolder -File -Include '*.xml', '*.pro' |
Group-Object -Property BaseName | Where-Object { $_.Count -gt 1 }
if ($duplicates) {
# output the duplicate XML to Flag_Duplicate.txt
$duplicates.Group | Where-Object {$_.Extension -eq '.xml' } | ForEach-Object {
$_.FullName | Out-File -FilePath $dupeReport -Append
}
# do the rest of your code
Write-Host "Flag duplicated, continue for Error_Monitoring"
Error_Monitoring
}
else {
Write-Host "Flag does not duplicate, continue the process"
}
Your script does not iterate correctly. You need to have an iteration to check. The Test-Path logic looks mixed up to me. I tried to keep as much of your code as possible.
This script checks for a any xml basename filename against any suffix duplicate (not only pro):
$Flag_Path = "C:\dir_to_be_checked"
$xmlFilesArray = #()
$allFilesExceptXml = #() # all files excluding xml files
# Get all the xml files
Get-ChildItem -Path $Flag_Path -Include "*.xml" | ForEach-Object { $xmlFilesArray += $_.basename }
# Get all files from the directory the xml files
Get-ChildItem -Path $Flag_Path -Exclude "*.xml" | ForEach-Object { $allFilesExceptXml += $_.basename }
# Iterate over list of files names without suffix
ForEach ($xmlFile in $xmlFilesArray) {
ForEach ($fileToCheck in $allFilesExceptXml) {
If ($xmlFile -eq $fileToCheck) {
# logging the duplicate file (specifying utf8 or the output would be UTF-16)
Write-Output "$Flag_Path\$xmlFile.xml" | Out-File -Append -Encoding utf8 $Flag_Path\Flag_Duplicate
Write-Host "Flag duplicated, continue with duplicate search"
# pause
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Error_Monitoring
} Else {
Write-Host "Flag is not duplicated. Continue with the search."
}
}
}
I have a folder full of 500,00+ files. I'm trying to iterate through this folder and run some logic to determine if we can delete unneeded files. The problem is this process needs to run semi-regularly and the new files that need to be deleted are currently at the end of the list it seems.
I put together the following list of code to sort through it all:
gci $RPT | %{
$flag = 0;
$number = [int]($_.Name | select-string -pattern "\d{12}" -Allmatches).Matches.Value
if ($submidlist -match "^$number$"){
if ($_ -notmatch "acct\.csv|jpd\.csv|jss\.pdf|jman\.pdf|3600\.pdf|cont\.pdf|msl\.txt|pres\.pdf|tray\.pdf|qual\.pdf|zipl\.pdf"){
echo "DELETE SUBMID $_"
remove-item $RPT\$_
$count++
$totalcount++
$flag = 1;
}
}
if ($jobidlist -match "^$number$"){
if ($_ -match "acct\.csv|jpd\.csv|jss\.pdf|jman\.pdf|3600\.pdf|cont\.pdf|msl\.txt|pres\.pdf|tray\.pdf|qual\.pdf|zipl\.pdf"){
echo "DELETE JOBID $_"
remove-item $RPT\$_
$count++
$totalcount++
$flag = 1;
}
}
}
Currently, running the above script takes over 24 hours and it still doesn't make it to the end of the list. Is there a way to optimize this or reverse the order that get-childitem iterates through this folder?
function Delete-Items($List, [string]$ListName){
$DoNotDelete = #("acct.csv","jpd.csv","jss.pdf","jman.pdf","3600.pdf","cont.pdf","msl.txt","pres.pdf","tray.pdf","qual.pdf","zipl.pdf")
$List = $List | %{
"*$_*"
}
Get-ChildItem C:\TEST\56381643\ -Recurse -Include $List -Directory | %{
Get-ChildItem $_.FullName -Exclude $DoNotDelete -Recurse | %{
echo "DELETE $ListName $($_.name | select-string -pattern "\d{12}")"
Remove-Item -Path $_.FullName -WhatIf
}
}
}
#Example Usage
$JobList = #(
098765432109
123456789012
)
$SubmitList = #(
234567890123
)
Delete-Items -List $JobList -ListName JOBID
Delete-Items -List $SubmitList -ListName SUBMID
Lets go over a basic rundown of whats happening in the function.
We have a array of files not to delete
We turn the $list numbers into wildcards by adding a * before and after each item in the array. We then only search for those directories that contain those numbers.
We then use another Get-ChildItem to get the files in each directory but exclude the ones mentioned in$DoNotDelete`.
If you want to delete the files delete the -Whatif on the remove-item
I'm trying to write to the output (echo) within two pipe to have a trace of what I'm doing but I have an error.
Get-ChildItem $path -Recurse |
Where-Object { $_.Name -match '.+?\.log\.(\d{4})-(\d{2})-(\d{2})$' -and $_.LastWriteTime -lt $deleteDate } |
Write-Output "deleting file" -PassThru |
Remove-Item
but I have this error :
Write-Output : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
I also tested in the Foreach-Object function
Get-ChildItem $path -Recurse |
Where-Object { $_.Name -match '.+?\.log\.(\d{4})-(\d{2})-(\d{2})$' -and $_.LastWriteTime -lt $deleteDate } |
Foreach-Object {
echo "deleting $($_.Name)";
Remove-Item($_);
}
but here nothing is printed in the console (but the script finish without error and deleted the files I wanted)
So how can I print to thje output wihtin two Pipe ?
Thx
Write-Output sends output to the pipeline. It accepts input via pipeline or argument, but not both, so you can do this:
Write-Output "Test"
or this:
"Test" | Write-Output
but not this:
"Test" | Write-Output "Test"
Solution:
Get-ChildItem $path -Recurse | where {
$_.Name -match '.+?\.log\.(\d{4})-(\d{2})-(\d{2})$' -and $_.LastWriteTime -lt $deleteDate
} | foreach {
# Write-Host writes to the console only
Write-Host "Deleting $($_.Name)"
Remove-Item $_
}
Mind that Remove-Item expects a string argument, so everything you pass will be converted to string. Casting a FileInfo object to string might not always return the full path (because of its implementation of ToString()) depending on how it was created!
In the example above, it is safe to use, but this for instance will throw an an exception (if your current working directory is not $path):
(Get-Item $path).GetFiles() | foreach { Remove-Item $_ }
So it's always safest to use $_.FullName.
In the most basic sense, I have a SQL query which returns an array of IDs, which I've stored into a variable $ID. I then want to perform a Get-childitem on a specific folder for any filenames that contain any of the IDs in said variable ($ID) There are three possible filenames that could exist:
$ID.xml
$ID_input.xml
$ID_output.xml
Once I have the results of get-childitem, I want to output this as a text file and delete the files from the folder. The part I'm having trouble with is filtering the results of get-childitem to define the filenames I'm looking for, so that only files that contain the IDs from the SQL output are displayed in my get-childitem results.
I found another way of doing this, which works fine, by using for-each ($i in $id), then building the desired filenames from that and performing a remove item on them:
# Build list of XML files
$XMLFile = foreach ($I in $ID)
{
"$XMLPath\$I.xml","$XMLPath\$I`_output.xml","$XMLPath\$I`_input.xml"
}
# Delete XML files
$XMLFile | Remove-Item -Force
However, this produces a lot of errors in the shell, as it tries to delete files that don't exist, but whose IDs do exist in the database. I also can't figure out how to produce a text output of the files that were actually deleted, doing it this way, so I'd like to get back to the get-childitem approach, if possible.
Any ideas would be greatly appreciated. If you require more info, just ask.
You can find all *.xml files with Get-ChildItem to minimize the number of files to test and then use regex to match the filenames. It's faster than a loop/multiple test, but harder to read if you're not familiar with regex.
$id = 123,111
#Create regex-pattern (search-pattern)
$regex = "^($(($id | ForEach-Object { [regex]::Escape($_) }) -join '|'))(?:_input|_output)?$"
$filesToDelete = Get-ChildItem -Path "c:\users\frode\Desktop\test" -Filter "*.xml" | Where-Object { $_.BaseName -match $regex }
#Save list of files
$filesToDelete | Select-Object -ExpandProperty FullName | Out-File "deletedfiles.txt" -Append
#Remove files (remove -WhatIf when ready)
$filesToDelete | Remove-Item -Force -WhatIf
Regex demo: https://regex101.com/r/dS2dJ5/2
Try this:
clear
$ID = "a", "b", "c"
$filesToDelete = New-Object System.Collections.ArrayList
$files = Get-ChildItem e:\
foreach ($I in $ID)
{
($files | Where-object { $_.Name -eq "$ID.xml" }).FullName | ForEach-Object { $filesToDelete.Add($_) }
($files | Where-object { $_.Name -eq "$ID_input.xml" }).FullName | ForEach-Object { $filesToDelete.Add($_) }
($files | Where-object { $_.Name -eq "$ID_output.xml" }).FullName | ForEach-Object { $filesToDelete.Add($_) }
}
$filesToDelete | select-object -Unique | ForEach-Object { Remove-Item $_ -Force }