It sounds complex but its not, Comparing Files in PowerShell - powershell

I have a folder with a huge amount of files in it with lots of different extensions (abc, abc_trg, def, def_trg, ghi, ghi_trg, jkl, mno).
You will see that there are some files that have a matching 'trigger' file, but not all files in this folder need to have a trigger file, it is just the following extensions that must have a trigger file: abc, def, ghi.
filename1.abc
filename1.abc_trg
filename2.def
filename2.def_trg
filename3.abc
filename4.def_trg
filename5.ghi
filename6.jkl
filename7.mno
filename8.ghi
filename8.ghi_trg
filename9.jkl
i.e. The extension types that do have Trigger files (abc, abc_trg, def, def_trg, ghi, ghi_trg) must have a matching filename.
I need a PowerShell script that will analyse the and compare files that are meant to exist with a trigger filetype (abc, abc_trg, def, def_trg, ghi, ghi_trg) and if a match is found (e.g. filename1, filename2, filename8) or if there are files that have extensions not in this list, e.g. jkl & mno (filename6.jkl, filename7.mno, filename9.jkl) then those files are left/not touched.
If there are files that are meant to have a matching extension & trigger file, but do not, i.e. they have become orphaned, then these need to be deleted (e.g. filename3.abc, filename4.def_trg, filename5.ghi)
So the resultant file list should look like this:
filename1.abc
filename1.abc_trg
filename2.def
filename2.def_trg
filename6.jkl
filename7.mno
filename8.ghi
filename8.ghi_trg
filename9.jkl
Here is my code so far:
$strDir = "D:\Temp\FileCompareTest\"
$strFileTypesToIgnore = ".jkl",".mno"
$strExtABC = ".abc"
$strExtABC_Trigger = ".abc_trg"
$strExtDEF = ".def"
$strExtDEF_Trigger = ".def_trg"
$strExtGHI = ".ghi"
$strExtGHI_Trigger = ".ghi_trg"
$arrFiles = Get-ChildItem $strDir -exclude $strFileTypesToIgnore
ForEach ($objFile in $arrFiles) {
$strFilename = $objFile.BaseName
$strExtension = $objFile.Extension
If ($strExtension -eq ".abc") {
$arrFiles2 = Get-ChildItem $strDir -exclude $strFileTypesToIgnore
ForEach ($objFile2 in $arrFiles2) {
$strFilename2 = $objFile2.BaseName
$strExtension2 = $objFile2.Extension
If ($strExtension2 -eq ".abc_trg") {
If (Compare-Object $strFilename $strFilename2) {
Write-Host "match is: $strFilename$strExtension and $strFilename2$strExtension2"
} Else {
Write-Host "Not a match: $strFilename$strExtension and $strFilename2$strExtension2"
}
}
}
}
}
Can you help please?
Regards
Darren

Thanks for including your code. You have the right idea of comparing names and and extensions but lets try a different approach.
We loop each file and check its extension. Depending on if the file is a trg file or not we have a similar mean to check for its partner. Since we use a Where-Object clause the output files are passed onto the pipe to Remove-Item for deletion. Test with the -WhatIf switch to verify it is working.
I tried to use simple cmdlets and methods for string manipulation.
$path = "C:\temp\test"
$extension = "_trg"
$files = Get-ChildItem -Path $path
$fileNames = $files | Select-Object -ExpandProperty Name
$files | Where-Object{
# Check what extension this file is so we can find the appropriate partner
if($_.Extension.Contains($extension)){
# Attempt to find a matching non trg file
$fileNames -notcontains $_.Name.Substring(0, $_.Name.LastIndexOf($extension))
} else {
# Attempt to find a matching trg file
$fileNames -notcontains "$($_.Name)$extension"
}
} | Remove-Item -Force -Confirm:$false -WhatIf
We save all the file names in $fileNames and use -notcontains to see if the file in the loop has its partner in the list. If not it passed through the pipe.

Related

Find files with partial name match and remove desired file

I have a little over 12000 files that I need to sort through.
18-100-00000-LOD-H.pdf
18-100-00000-LOD-H-1C.pdf
21-200-21197-LOD-H.pdf
21-200-21197-LOD-H-1C.pdf
21-200-21198-LOD-H.pdf
21-200-21198-LOD-H-1C.pdf
I need a way to go through all the files and delete the LOD-H version of the files.
EX:
21-200-21198-LOD-H.pdf
21-200-21198-LOD-H-1C.pdf
With the partial match being the 5 digit code I need a script that would delete the LOD-H case of the partial match.
So far this is what I have but it won't work because I need to supply values for the pattern but since there isn't one set pattern and more like multiple patterns I don't know what to supply it with
$source = "\\Summerhall\GLUONPREP\Market Centers\~Pen Project\Logos\ALL Office Logos"
$destination = "C:\Users\joshh\Documents\EmptySpace"
$toDelete = "C:\Users\joshh\Documents\toDelete"
$allFiles = #(Get-ChildItem $source -File | Select-Object -ExpandProperty FullName)
foreach($file in $allFiles) {
$content = Get-Content -Path $file
if($content | Select-String -SimpleMatch -Quiet){
$dest = $destination
}
else{
$dest = $toDelete
}
}
Any help would be super appreciated, even links to something similar or even links to documentation so I can start piecing a script of my own would be super helpful.
Thank you!
This should work for what you need:
# Get a list of the files with -1C preceeding the extension
$1cFiles = #( ( Get-ChildItem -File "${source}/*-LOD-H-1C.pdf" ).Name )
# Retreive files that match the same pattern without 1C, and iterate over them
Get-ChildItem -File "${source}/*-LOD-H.pdf" | ForEach-Object {
# Get the name of the file if it had the -1C suffix preceeding the .ext
$useName = $_.Name.Insert($_.Name.LastIndexOf('.pdf'), '-1C')
# If the -1C version of the file exists, remove the current (non-1C) file
if( $1cFiles -contains $useName ) {
Remove-Item -Force $_
}
}
Basically, look for the 1C files in $toDelete, then iterate over the non-1C files in $toDelete, removing the non-1C file if adding -1C before the file extension matches an existing file with 1C in the name.

Search and replace files and folders names with txt file support

I have many folders and inside these different files. Each folder and their children files have the same name and different extension, so in the ABC folder there are the ABC.png, ABC.prj, ABC.pgw files, in the DEF folder there are the DEF.png, DEF.prj, DEF.pgw files and so on.
With a script I have created a txt file with the list of png file names. Then I put in row 2 a new name for the name in row1, in row 4 a new name for the name in row 3, and so on.
Now I'm searching a powershell script that:
- scan all folder for the name in row 1 and replace it with name in row2
- scan all folder for the name in row 3 and replace it with name in row4 and so on
I have try with this below, but it doesn't work.
Have you some suggestions? Thank you
$0=0
$1=1
do {
$find=Get-Content C:\1\Srv\MapsName.txt | Select -Index $0
$repl=Get-Content C:\1\Srv\MapsName.txt | Select -Index $1
Get-ChildItem C:\1\newmaps -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
until ($0 -eq "")
I believe there are several things wrong with your code and also the code Manuel gave you.
Although you have a list of old filenames and new filenames, you are not using that in the Get-ChildItem cmdlet, but instead try and replace all files it finds.
Using -replace uses a Regular Expression replace, that means the special character . inside the filename is regarded as Any Character, not simply a dot.
You are trying to find *.png files, but you do not add a -Filter with the Get-ChildItem cmdlet, so now it will return all filetypes.
Anyway, I have a different approach for you:
If your input file C:\1\Srv\MapsName.txt looks anything like this:
picture1.png
ABC_1.png
picture2.png
DEF_1.png
picture3.png
DEF_2.png
The following code will use that to build a lookup Hashtable so it can act on the files mentioned in the input file and leave all others unchanged.
$mapsFile = 'C:\1\Srv\2_MapsName.txt'
$searchPath = 'C:\1\NewMaps'
# Read the input file as an array of strings.
# Every even index contains the file name to search for.
# Every odd index number has the new name for that file.
$lines = Get-Content $mapsFile
# Create a hashtable to store the filename to find
# as Key, and the replacement name as Value
$lookup = #{}
for ($index = 0; $index -lt $lines.Count -1; $index += 2) {
$lookup[$lines[$index]] = $lines[$index + 1]
}
# Next, get a collection of FileInfo objects of *.png files
# If you need to get multiple extensions, remove the -Filter and add -Include '*.png','*.jpg' etc.
$files = Get-ChildItem -Path $searchPath -Filter '*.png' -File -Recurse
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
$find = $file.Name
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
Write-Host "Renaming '$($file.FullName)' --> $($lookup[$find])"
$file | Rename-Item -NewName $lookup[$find]
}
}
Edit
If the input text file 'C:\1\Srv\MapsName.txt' does NOT contain filenames including their extension, change the final foreach loop into this:
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
# Look for the file name without extension as it is not given in the 'MapsName.txt' file.
$find = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
# Make sure to add the file's extension if any.
$newName = $lookup[$find] + $file.Extension
Write-Host "Renaming '$($file.FullName)' --> '$newName'"
$file | Rename-Item -NewName $newName
}
}
Hope that helps
The problem in your snippet is that it never ends.
I tried it and it works but keeps looping forever.
I created a folder with the files a.txt, b.txt and c.txt.
And in the map.txt I have this content:
a.txt
a2.md
b.txt
b2.md
c.txt
c2.md
Running the following script I managed to rename every file to be as expected.
$0=0
$1=1
$find=Get-Content D:\map.txt | Select -Index $0
while($find) {
$find=Get-Content D:\map.txt | Select -Index $0
$repl=Get-Content D:\map.txt | Select -Index $1
if(!$find -Or !$repl) {
break;
}
Get-ChildItem D:\Files -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}

Powershell check if file exists in multiple folders and Output

I have a few hundred folders that look like this:
\\\uat.xxx.com\FileExport\New Collections\LCTS
\\\uat.xxx.com\FileExport\New Collections\GBSS
\\\uat.xxx.com\FileExport\New Collections\TRGS
etc
I need to check them for a specific file e.g. "Results 20150722New.dat"
I need to know the folders that do not contain the file, it may be nice if that can be outputted to a file e.g. log.txt but its more I need a list of folders that do not contain it.
I have been trying to use Test-Path but am really not getting anywhere
any chance someone could help me make a start on this
As one time operation you can find names (string) of all directories, that does not contain such file name, using:
Get-ChildItem "\\uat.xxx.com\FileExport\New Collections\" |
Where {$_.PSIsContainer } |
ForEach { if (-not(Test-Path "$($_.FullName)\Results 20150722New.dat")) {Echo $_.FullName } }
Optionally specify -Recurse switch to search folders recursively.
If there's a need to manipulate with results later, I'd prefer to save DirectoryInfo objects to a clollection instead of converting them to strings with Echo cmdlet.
$dirs_not_containing_file = #()
$dirs_not_containing_file +=
Get-ChildItem "\\uat.xxx.com\FileExport\New Collections\" |
Where {$_.PSIsContainer } |
ForEach { if (-not(Test-Path "$($_.FullName)\Results 20150722New.dat")) {$_} }
Splitted second statement to multiple lines for readability.

Powershell - Assigning unique file names to duplicated files using list inside a .csv or .txt

I have limited experience with Powershell doing very basic tasks by itself (such as simple renaming or moving files), but I've never created one that has the need to actually extract information from inside a file and apply that data directly to a file name.
I'd like to create a script that can reference a simple .csv or text file containing a list of unique identifiers and have it assign those to a batch of duplicated files (they all have the same contents) that share a slightly different name in the form of a 3-digit number appended as the prefix of a generic name.
For example, let's say my list of files are something like this:
001_test.txt
002_test.txt
003_test.txt
004_test.txt
005_test.txt
etc.
Then my .csv contains an alphabetical list of what I would like those to become:
Alpha.txt
Beta.txt
Charlie.txt
Delta.txt
Echo.txt
etc.
I tried looking at similar examples, but I'm failing miserably trying to tailor them to get it to do the above.
EDIT: I didn't save what I already modified, but here is the baseline script I was messing with:
$file_server = Read-Host "Enter the file server IP address"
$rootFolder = 'C:\TEMP\GPO\source\5'
Get-ChildItem -LiteralPath $rootFolder -Directory |
Where-Object { $_.Name -as [System.Guid] } |
ForEach-Object {
$directory = $_.FullName
(Get-Content "$directory\gpreport.xml") |
ForEach-Object { $_ -replace "99.999.999.999", $file_server } |
Set-Content "$directory\gpreport.xml"
# ... etc
}
I think this is to replace a string inside a file though. I need to replace the file name itself using a list from another file (that is not getting renamed), while not changing the contents of the files that are being renamed.
So you want to rename similar files with those listed in a text file. Ok, here's what you are going to need for my solution (alias listed in parenthesis): Get-Content (GC), Get-ChildItem (GCI), Where (?), Rename-Item, ForEach (%)
$NewNames = GC c:\temp\Namelist.txt #Path, including file name, to list of new names
$Name = "dog.txt" #File name without the 001_ prefix
$Path = "C:\Temp" #Path to search
$i=0
GCI $path | ?{$_.Name -match "\d{3}_$Name"}|%{Rename-Item $_.FullName $NewNames[$i];$i++}
Tested as working. That gets your list of new names and saves it as an array. Then it defines your file name, path, and sets $i to 0 as a counter. Then for each file that matches your pattern it renames it based off of item number $i in the array of new names, and then increments $i up one number and moves to the next file.
I haven't tested this, but it should be pretty close. It assumes you have a CSV with a column named FileNames and that you have at least as many names in that list as there are on disk.
$newNames = Import-Csv newfilenames.csv | Select -ExpandProperty FileNames
$existingFiles = Get-ChildItem c:\someplace
for ($i = 0; $i -lt $existingFiles.count; $i++)
{
Rename-Item -Path $existingFiles[$i].FullName -NewName $newNames[$i]
}
Basically, you create two arrays and using a basic for loop steping through the list of files on disk and pull the name from the corresponding index in the newNames array.
Does your CSV file map the identifiers to the file names?
Identifier,NewName
001,Alpha
002,Beta
If so, you'll need to look up the identifier before renaming the file:
# Define the naming convention
$Suffix = '_test'
$Extension = 'txt'
# Get the files and what to rename them to
$Files = Get-ChildItem "*$Suffix.$Extension"
$Csv = Import-Csv 'Names.csv'
# Rename the files
foreach ($File in $Files) {
$NewName = ($Csv | Where-Object { $File.Name -match '^' + $_.Identifier } | Select-Object -ExpandProperty NewName)
Rename-Item $File "$NewName.$Extension"
}
If your CSV file is just a sequential list of filenames, logicaldiagram's answer is probably more along the lines of what you're looking for.

Renaming a new folder file to the next incremental number with powershell script

I would really appreciate your help with this
I should first mention that I have been unable to find any specific solutions and I am very new to programming with powershell, hence my request
I wish to write (and later schedule) a script in powershell that looks for a file with a specific name - RFUNNEL and then renames this to R0000001. There will only be one of such 'RFUNELL' files in the folder at any time. However when next the script is run and finds a new RFUNNEL file I will this to be renamed to R0000002 and so on and so forth
I have struggled with this for some weeks now and the seemingly similar solutions that I have come across have not been of much help - perhaps because of my admittedly limited experience with powershell.
Others might be able to do this with less syntax, but try this:
$rootpath = "C:\derp"
if (Test-Path "$rootpath\RFUNNEL.txt")
{ $maxfile = Get-ChildItem $rootpath | ?{$_.BaseName -like "R[0-9][0-9][0-9][0-9][0-9][0-9][0-9]"} | Sort BaseName -Descending | Select -First 1 -Expand BaseName;
if (!$maxfile) { $maxfile = "R0000000" }
[int32]$filenumberint = $maxfile.substring(1); $filenumberint++
[string]$filenumberstring = ($filenumberint).ToString("0000000");
[string]$newName = ("R" + $filenumberstring + ".txt");
Rename-Item "$rootpath\RFUNNEL.txt" $newName;
}
Here's an alternative using regex:
[cmdletbinding()]
param()
$triggerFile = "RFUNNEL.txt"
$searchPattern = "R*.txt"
$nextAvailable = 0
# If the trigger file exists
if (Test-Path -Path $triggerFile)
{
# Get a list of files matching search pattern
$files = Get-ChildItem "$searchPattern" -exclude "$triggerFile"
if ($files)
{
# store the filenames in a simple array
$files = $files | select -expandProperty Name
$files | Write-Verbose
# Get next available file by carrying out a
# regex replace to extract the numeric part of the file and get the maximum number
$nextAvailable = ($files -replace '([a-z])(.*).txt', '$2' | measure-object -max).Maximum
}
# Add one to either the max or zero
$nextAvailable++
# Format the resulting string with leading zeros
$nextAvailableFileName = 'R{0:000000#}.txt' -f $nextAvailable
Write-Verbose "Next Available File: $nextAvailableFileName"
# rename the file
Rename-Item -Path $triggerFile -NewName $nextAvailableFileName
}