I have a script which will compare two very similar directories to see which has newer, updated files. The two paths have the same files but the path names are locations that are slightly different. The two folders have about the same amount of files, maybe two or three less than the other.
$path1 = "E:\docs\training\files"
$path2 = "D:\docs\training - Copy\files"
$outdatedFiles = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
if($file2.BaseName -match $file.BaseName)
{
if($file.LastWriteTime -gt $file2.LastWriteTime)
{
$Result = "" | Select OutDatedFile,LastWriteTime
$Result.OutDatedFile = $file2.FullName
$Result.LastWriteTime = $file2.LastWriteTime
$outdatedFiles += $Result
}
}
}
}
In the $outdatedFiles array, I get files that are not newer than their counterpart in the other directory. I think it might be due to my comparison in the if statement, I tried -match, -contains, and -ccontains to see if any of these would give me what I wanted. Neither worked. It might be that the foreach doesn't work due to the slightly different amount of files in each folder. Any suggestions?
EDIT
I tried building a hash but this did not find all the updated files:
$outdatedFiles = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
if($file2.Name -like $file.Name)
{
#compare hash here
$Hash2 = Get-FileHash $file2.FullName -Algorithm SHA256
$Hash1 = Get-FileHash $file.FullName -Algorithm SHA256
if($Hash2.Hash -ne $Hash1.Hash)
{
$Result = "" | Select OutDatedFile,LastWriteTime
$Result.OutDatedFile = $file2.FullName
$Result.LastWriteTime = $file2.LastWriteTime
$outdatedFiles += $Result
}
}
}
}
EDIT
This was my solution
$Differences1 = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
<#Trim path then compare#>
#File1
$file1part1 = ($file.FullName).Split("\")[-2]
$file1part2 = ($file.FullName).Split("\")[-1]
$newPath1 = $file1part1 + "\" + $file1part2
#File2
$file2part1 = ($file2.FullName).Split("\")[-2]
$file2part2 = ($file2.FullName).Split("\")[-1]
$newPath2 = $file2part1 + "\" + $file2part2
if($newPath1 -like $newPath2)
{
$Differences1 += Compare-Object (gci $file2.FullName) -DifferenceObject (gci $file.FullName) -Property LastWriteTime -PassThru | Select Name,FullName,LastWriteTime | Sort-Object -Property Name
}
}
}
if($Differences1 -ne $null)
{
$Differences1 | Out-File $textFile -Append
}
else
{
"No folders have different modified dates" | Out-File $textFile -Append
}
In the end, the solution was more complex than I wanted, or maybe I just made it that way.
The problem was I had multiple files with the same name and similar paths, as in the subfolder was named the same. I had to trim the path to be able to get a better comparison:
$Differences1 = #()
foreach($file in $Folder1)
{
foreach($file2 in $Folder2)
{
<#Trim path then compare#>
#File1
$file1part1 = ($file.FullName).Split("\")[-2]
$file1part2 = ($file.FullName).Split("\")[-1]
$newPath1 = $file1part1 + "\" + $file1part2
#File2
$file2part1 = ($file2.FullName).Split("\")[-2]
$file2part2 = ($file2.FullName).Split("\")[-1]
$newPath2 = $file2part1 + "\" + $file2part2
if($newPath1 -like $newPath2)
{
$Differences1 += Compare-Object (gci $file2.FullName) -DifferenceObject (gci $file.FullName) -Property LastWriteTime -PassThru | Select Name,FullName,LastWriteTime | Sort-Object -Property Name
}
}
}
if($Differences1 -ne $null)
{
$Differences1 | Out-File $textFile -Append
}
else
{
"No folders have different modified dates" | Out-File $textFile -Append
}
Related
I am recursively getting a list of folders with their respective permissions in a powershell script, however when the recursive part happens my output string keeps printing the folder structure each time an example of this is:
I have a folder called C:\temp, within that folder are 2 empty folders C:\temp\folder1 and C:\temp\folder2. With my script the output would be:
I have left out the permissions for readability
C:\temp
C:\temp\folder1
C:\temp
C:\temp\folder2
I don't want this to happen I want a list of folders with their permissions and then if the permissions on a child folder are different then look at the get the child folders of that folder. This works apart from the string building which I think I need a fresh pair of eyes to look at it because I'm getting nowhere.
Appreciate the help in advance,
Sam
CODE:
Add-Type -AssemblyName System.Windows.Forms
Import-Module ActiveDirectory
$info = ""
$OutputString
$step = 0
function DisplayForm{
#Some GUI code
#$textBox takes in the base folder from the user
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$baseFolder = $textBox.Text
$ParentProperties = (Get-Acl $baseFolder).Access| Select-Object -ExpandProperty IdentityReference
$OutputString = $OutputString + $baseFolder + "`r`n" + $ParentProperties + "`r`n`r`n"
$ChildFolders = Get-ChildItem $baseFolder | where {$_.Attributes -eq 'Directory'}
FindPriorities($baseFolder)
$info = "SAVED TO FOLDER"
outputList
}
}
function FindPriorities{
param($fileName)
$ChildFolders = Get-ChildItem $fileName | where {$_.Attributes -eq 'Directory'}
$step = $step + 1
$TempString = ""
foreach ($folder in $ChildFolders){
$child = $fileName + "\\" + $folder.name
$ParentProperties = (Get-Acl $fileName).Access| Select-Object -ExpandProperty IdentityReference
$ChildProperties = (Get-Acl $child).Access| Select-Object -ExpandProperty IdentityReference
$parentString=""
foreach ($p in $ParentProperties){
$parentString= $parentString + $p
}
$childString=""
foreach ($c in $childProperties){
$childString = $childString + $c
}
if($childString -ne $parentString){
$OutputString = $OutputString + $child + "`r`n" + $ChildProperties + "`r`n`r`n"
FindPriorities ($child)
}else{
$OutputString = $OutputString + $child + "`r`n" + $ChildProperties + "`r`n`r`n"
}
}
}
function outputList{
$OutputString
}
DisplayForm
I think I understood what you want to do.
Please give this snippet a try:
function Get-IdentityReference($path) {
Get-Acl $path |
Select-Object -ExpandProperty Access |
Select-Object -ExpandProperty IdentityReference
}
function Extract-Permissions($baseFolder) {
$folders = Get-ChildItem $baseFolder | Where-Object { $_.PSisContainer }
$baseACL = Get-IdentityReference $baseFolder
"$baseFolder : $baseACL"
foreach($folder in $folders) {
$folderACL = Get-IdentityReference $folder.FullName
$childFolders = Get-ChildItem $folder.FullName | Where-Object { $_.PSisContainer }
"$($folder.FullName) : $folderACL"
foreach($childFolder in $childFolders) {
$childACL = Get-IdentityReference $childFolder.FullName
if(Compare-Object $childACL $folderACL) {
Extract-Permissions $childFolder.FullName
} else {
"$($childFolder.FullName) : $childACL"
}
}
}
}
$baseFolder = "$env:USERPROFILE\Desktop"
Extract-Permissions $baseFolder
I have a directory that our work order program dumps xml files into. I need to search those files for a specific string and then copy them to another location based on that string. I modified the below code from another post and while I don't get any errors it also doesn't work. I'm very much a scripting newbie so any help would be greatly appreciated.
[string] $FileDirectory = "D:\Temp";
[string] $OutputPath = "D:\Temp\Temp_NY";
[string] $OutputPath2 = "D:\Temp\TEMP_FL";
foreach ($FilePath in Get-ChildItem $FileDirectory | Select-Object -ExpandProperty FullName)
{
[string] $Header = Get-Content $FilePath -First 0
if ($Header -match 'PARTNER |TEST_NY') {
Copy-Item $FilePath $OutputPath
}
elseif ($Header -match 'PARTNER |TEST_FL*') {
Copy-Item $FilePath $OutputPath2
}
}
The header would be -First 1 (first line only). -First 0 returns nothing. Try:
$FileDirectory = "D:\Temp";
$OutputPath = "D:\Temp\Temp_NY";
$OutputPath2 = "D:\Temp\TEMP_FL";
Get-ChildItem $FileDirectory | ? { !$_.PSIsContainer } | ForEach-Object {
$FilePath = $_.FullName
$Header = Get-Content $FilePath -First 1
if ($Header -match 'PARTNER |TEST_NY') {
Copy-Item $FilePath $OutputPath
}
elseif ($Header -match 'PARTNER |TEST_FL*') {
Copy-Item $FilePath $OutputPath2
}
}
I m trying to write a script to compare permissions of two folders at two different locations. The Folder name at both locations would be the ADID of an user and some users may have two or more AD accounts.
$OutPath = ".\out1.csv"
$sourcepath1 = Get-Content ".\mwd_src.txt"
foreach ($path1 in $sourcepath1) {
$name1 = (Get-Item $Path1).name
$FolderAcl1 = (Get-Acl $Path1).Access | Select-Object IdentityReference
$Source = $FolderAcl1 | Where-Object { $_.IdentityReference -like "*$name1*"} |
Select-Object #{ Label = "Path"; Expression = { echo $Path1 } }, #{ Label = "Access"; Expression = { $_.IdentityReference } }
}
$sourcepath2 = Get-Content ".\mwd_dest.txt"
foreach ($path2 in $sourcepath2) {
$name2 = (Get-Item $Path2).Name
$FolderAcl2 = (Get-Acl $Path2).Access | Select-Object IdentityReference
$Dest = $FolderAcl2 | Where-Object { $_.IdentityReference -like "*$name2*" } |
Select-Object #{ Label = "Path"; Expression = { echo $Path2 } }, #{ Label = "Access"; Expression = { $_.IdentityReference } }
}
$Source1 = $Source | Select-Object -Unique
$Dest1 = $Dest | Select-Object -Unique
$Out = Compare-Object -ReferenceObject $Source1 -DifferenceObject $Dest1
$Out1 = $Out | Where-Object { $_.SideIndicator -match "=>" }
foreach($OutItem in $Out1) {
$Outitem.InputObject | Add-Content $OutPath
}
But getting the following error
"Compare-Object : Cannot bind argument to parameter 'DifferenceObject' because it is null.
"
Please assist.
As I am missing information to your paths, I cannot reproduce your code but I tried to compare the ACLs for two local files with Compare-Object. I create two files which initially had the same accesses. If there is no difference, nothing is returned. I removed my account then from the permissions of the second file and got the output. (I shortened the output to avoid a horizontal bar.) The SideIndicator shows with the arrow to which object the difference belong.
InputObject SideIndicator
----------- -------------
{System.Security.AccessControl.FileSystemAccessRule, System.Security... =>
{System.Security.AccessControl.FileSystemAccessRule, System.Security... <=
My test code:
$ref = Get-Acl -Path .\Desktop\test1.txt
$diff = Get-Acl - Path .\Desktop\test2.txt
Compare-Object -ReferenceObject $ref.Access -DifferenceObject $diff.Access
I selected the Access property for comparison.
Regarding your error, ensure that the DifferenceObject is populated with the information you expect.
I am trying to compare multiple files against a single document. I have managed to make that part work however where my issue is, is that i want to be able to check if the files exist before a comparison is run.
i.e. check if file A exists, if so compare against master csv file, if not continue on and check if file b exists, if so compare against master csv and so on.
my script so far goes:
$files = get-content -path "H:\Compare\File Location\servername Files.txt"
$prod = "H:\compare\Results\master_SystemInfo.csv"
foreach ($file in $files) {
If((Test-Path -path $file))
{
Write-Host "File exists, comparing against production"
$content1 = Get-Content "H:\Compare\Results\$file"
$content2 = Get-Content $prod
$comparedLines = Compare-Object $content1 $content2 -IncludeEqual |
Sort-Object { $_.InputObject.ReadCount }
$lineNumber = 0
$comparedLines | foreach {
$pattern = ".*"
if($_.SideIndicator -eq "==" -or $_.SideIndicator -eq "=>")
{
$lineNumber = $_.InputObject.ReadCount
}
if($_.InputObject -match $pattern)
{
if($_.SideIndicator -ne "==")
{
if($_.SideIndicator -eq "=>")
{
$lineOperation = "prod"
}
elseif($_.SideIndicator -eq "<=")
{
$lineOperation = "test"
}
[PSCustomObject] #{
Line = $lineNumber
File = $lineOperation
Text = $_.InputObject
}
}
}
} | Export-Csv "h:\compare\Comparison Reports\Prod.vs.$file" - NoTypeInformation
}
Else
{ "File does not exist, aborting" ; return}
}
The comparison is working just need to add the check for file before running comparison as it is still spitting out results for files that don't exist.
Thank you very much,
I have found the answer by altering the code, this time im just creating a txt file from the files in the folder first that way i don't need to test-path. This now generates a file list from the folder, then compares each file against the master file and outputs multiple files, one for each comparison saving it as the original filename i.e. "Prod.vs._SystemInfor.csv"
FYI - In the first line the abc123* is a variable i put in to look for specific server names within the folder and generate a file list based on those only. We have a number of servers all with similar naming conventions just the last 4 digits are different depending on where they are located.
Thanks
Working Powershell script:
Get-ChildItem -file abc123* H:\Compare\Results -Name | Out-File "H:\Compare\Results\Office Files.txt"
$officefiles = get-content -path "H:\Compare\results\Office Files.txt"
$officeprod = "H:\compare\Results\master_SystemInfo.csv"
foreach ($officefile in $officefiles) {
$content1 = Get-Content "H:\Compare\Results\$officefile"
$content2 = Get-Content $officeprod
$comparedLines = Compare-Object $content1 $content2 -IncludeEqual |
Sort-Object { $_.InputObject.ReadCount }
$lineNumber = 0
$comparedLines | foreach {
$pattern = ".*"
if($_.SideIndicator -eq "==" -or $_.SideIndicator -eq "=>")
{
$lineNumber = $_.InputObject.ReadCount
}
if($_.InputObject -match $pattern)
{
if($_.SideIndicator -ne "==")
{
if($_.SideIndicator -eq "=>")
{
$lineOperation = "prod"
}
elseif($_.SideIndicator -eq "<=")
{
$lineOperation = "test"
}
[PSCustomObject] #{
Line = $lineNumber
File = $lineOperation
Text = $_.InputObject
}
}
}
} | Export-Csv "h:\compare\Comparison Reports\Prod.vs.$officefile" -NoTypeInformation
}
I have a text file file_paths.txt that contains full paths on each line:
C:\MyFolder1\app1.exe
C:\MyFolder2\l1.dll
C:\MyFolder3\app2.exe
C:\MyFolder1\l2.dll
C:\MyFolder5\app3.exe
C:\MyFolder3\app4.exe
C:\MyFolder6\app5.exe
I also have file folders.txt that contains list of folders:
C:\MyFolder1
C:\MyFolder2
C:\MyFolder3
C:\MyFolder4
C:\MyFolder8
I need to iterate through the list of folders in folders.txt, match it with files in file_paths.txt and write the results to a file result.txt like this:
In C:\MyFolder1 more than one files has been found:
C:\MyFolder1\app1.exe
C:\MyFolder1\l2.dll
In C:\MyFolder2 one file has been:
C:\MyFolder2\l1.dll
In C:\MyFolder3 more than one files has been found:
C:\MyFolder3\app2.exe
C:\MyFolder3\app4.exe
In C:\MyFolder4 no files has been found.
In C:\MyFolder8 no files has been found.
My attempt that doesn't work:
$paths = [System.IO.File]::OpenText("file_paths.txt")
$folders = [System.IO.File]::OpenText("folders.txt")
$result = "result.txt"
try {
for(;;) {
$folder = $folders.ReadLine()
if ($folder -eq $null) { break }
"In ">> $folder >> ": `n" >> $result
for(;;) {
$path = $paths.ReadLine()
if ($path -eq $null) { break }
if ($path -contains $folder) {" ">>$path>>"`n">>$result }
}
}
} finally {
$paths.Close()
$folders.Close()
}
I would separate processing from reporting. First build a hashtable from the contents of folders.txt and add the lines from file_paths.txt to the matching keys:
$folders = #{}
Get-Content 'folders.txt' | ForEach-Object { $folders[$_] = #() }
Get-Content 'file_paths.txt' | ForEach-Object {
$line = $_
$($folders.Keys) | Where-Object {
$line -like "$_*"
} | ForEach-Object {
$folders[$_] += $line
}
}
Then you can output the resulting data structure like this:
$folders.Keys | ForEach-Object {
'In {0} {1} files have been found' -f $_, $folders[$_].Count
if ($folders[$_].Count -gt 0) {
$folders[$_] | ForEach-Object { "`t$_" }
}
} | Out-File 'result.txt'
Below is a script you can use to do exactly what you need.
Note the $folderPath and $filePath variables. Replace with absolute or relative (to where you execute the script) path of the file_paths.txt and folders.txt files.
$folderPath = 'folders.txt'
$filePath = 'file_paths.txt'
(Get-Content $folderPath).Split('`r`n') | ForEach-Object {
$folder = $_
$count = 0
$fileArray = #()
(Get-Content $filePath).Split('`r`n') | ForEach-Object {
$file = $_
if( $file | Select-String $folder -Quiet ) {
$count++
$fileArray += $file
}
}
if($count -ne 0) {
Write-Output "In $folder, $count files has been found."
$fileArray | ForEach-Object {
Write-Output "`t$_"
}
} else {
Write-Output "In $folder, no files has been found."
}
}