Two loops two files comparison - powershell - powershell

Trying to make 2 loops while reading 2 files, the purpose to take 1 value from first file compare it to 1,2,3rd.. etc values in second file, then go back to outside loop, take 2nd value and compare to all the values in second file sequencially, and so on.
Problem is it only runs comparison on first value in first file and stops.
Code:
Get-ExecutionPolicy
$main_list = [System.IO.File]::OpenText('c:\ping-workstations\main_list.txt')
$retired = [System.IO.File]::OpenText('c:\ping-workstations\retired.txt')
$main_i = 1
$retired_i = 1
[string]$main_list_line
[string]$retired_list_line
try
{
while(!$main_list.EndOfStream)
{
$main_list_line = $main_list.ReadLine()
while(!$retired.EndOfStream)
{
$retired_list_line = $retired.readline()
if($main_list_line -eq $retired_list_line)
{
write-host "`n`n`n match found for $main_list_line in main list `n`n`n`n"
add-content "c:\ping-workstations\matches-through-text-files.txt" "$main_list_line`n"
}
write-host "main iteration: $main_i / inside iteration: $retired_i`n - main value: $main_list_line ---- inside value: $retired_list_line`n`n"
$retired_1++
}
$main_i++
}
}
finally
{
$main_list.close()
$retired.close()
}

You need to re-open the $retired file after the inner loop finishes. As it is written now, once the inner loop traverses the $retired file, $retired.EndOfStream will forever remain true.
try
{
while(!$main_list.EndOfStream)
{
$main_list_line = $main_list.ReadLine()
while(!$retired.EndOfStream)
{
$retired_list_line = $retired.readline()
if($main_list_line -eq $retired_list_line)
{
write-host "`n`n`n match found for $main_list_line in main list `n`n`n`n"
add-content "c:\ping-workstations\matches-through-text-files.txt" "$main_list_line`n"
}
write-host "main iteration: $main_i / inside iteration: $retired_i`n - main value: $main_list_line ---- inside value: $retired_list_line`n`n"
$retired_1++
}
$main_i++
$retired.close()
$retired = [System.IO.File]::OpenText('c:\ping-workstations\retired.txt')
}
}
finally
{
$main_list.close()
$retired.close()
}
As a side note, have you looked into using the Powershell ISE? It has debugging capabilities; by setting a breakpoint and stepping through your code, I was able to spot the problem quickly.

Related

Getting data out of structure not working

I'm trying to get the data out of the data structure, and I can see it's in there, but the variable the info is assigned to is null.
Let me know if you need more info for the method that assigns $SizeBasedVideoStringDevice, but it looks like this:
$SizeBasedVideoStringDevice:
[Object[7]]:
[0]:#{Key=LOAD_MEDIA;VALUE=SYSTEM.OBJECT[]}
[1]:#{KEY=LOAD_MEDIAB;VALUE=}
[2]:#{KEY=LOAD_MEDIAC;VALUE=}
[3]:#{KEY=LOAD_MEDIAD;VALUE=SYSTEM.OBJECT[]}
...
So some of them have a Value, which is fine.
When I look closer at that first value, it looks like this:
Value: [Object[5]]
[0]: "LoadDialog.MediaA"
[1]: "LoadDialog.MediaB"
[2]: "LoadDialog.MediaC"
...
I have the following code, and for some reason it's not finding that first one. Once I get the data out, I will loop thru and process each Value.
$tmp = "LOAD_MEDIA" #this will match first in data structure
$sizeBasedVideoString = $SizeBasedVideoStringDevice.where{$_Key -eq $tmp}.Value ###this is null
if($null -ne $sizeBasedVideoString)
{
$resultDPS_sized = New-Object System.Collections.Generic.List[string]
foreach($vid in $sizeBasedVideoString)
{
#get vid location
$temp = ProcessDPSMDB -mdbLookupString $vid -mdbFilePath $dpsMdbPathFull
$resultDPS_sized.Add($temp)
}
}
This is using PowerShell 5.1 and VSCode.

Powershell excel paste vaule

I have one excel file with a few sheets in it. Im trying to combine all of them into one sheet. I have code that does this, but the issue im having now is now the sheets have formulas on them. So when it copies to the new sheet it doesnt copy over the values, but copies the formulas. I was reading online you can do this pastespecial, but i cant get it working. Does anyone have code on how can i copy one sheet to another, but keep the values. The one issue is the sheets have differnt amount of rows so im not sure how what the range would be. I have tried many things and i just can figure it out.
This is using the excel.application
To questions, the thing is i cant figure out the .PasteSpecial.
Here is the code now that copies it to a sheet called combine.
$wb = $excel.Workbooks.Open($location)
$newSheetName = 'Combine'
$xlCellTypeLastCell = 11
$targetSheet = $wb.Sheets.Add()
$targetSheet.Name = $newSheetName
$includeHeader = $true
foreach ($sh in $wb.Sheets) {
$statename = $sh.Name
if($sh.name -eq "Available" -or $sh.name -eq "Eligible"){
}else{
$sh.autofiltermode = $false
$sourceRange = $sh.UsedRange
if ($sourceRange.Rows.Count -le 1) { continue }
# if ($sourceRange.Rows.Count -le 1 -or $sh.Name -eq $newSheetName) {continue}
$targetLastCell = $targetSheet.UsedRange.SpecialCells($xlCellTypeLastCell)
if ($includeHeader) {
[Void]$sourceRange.PasteSpecial($targetLastCell)
}
else {
$columnOffset = - $targetLastCell.Column + 1
$targetCell = $targetLastCell.Offset(1, $columnOffset)
$newRowCount = $sourceRange.Rows.Count - 1
[Void]$sourceRange.Offset(1, 0).Resize($newRowCount).Copy($targetCell)
}
$includeHeader = $false
}
}
This does work with headers, but at this point i dont really care about those. I will say UsedRange also misses me up. So right now it i just use the .copy and from reading online i need to use the pasteSpecial.

Powershell Switch Break Label not being executed

As part of a larger script I have implemented the switch detailed below. The intention is that when the script is executed a user can choose to either
enter users to be migrated on-screen, or
import from a file.
If the import from file option is selected - I want to test the file is present - if not I want to break out and go back to the switch label :choose. However when I select the import from file option and provide a non-existent path the script continues and does not break or return to the label. Where am I going wrong?
$chooseInputMethod = #"
This script migrates one or more user accounts between two trusted domains in a forest e.g. from domain1 to domain2 (or vice versa)
Select method to specify user(s) to migrate:
1. Enter name(s) on-screen (default)
2. Import name(s) from file
Enter selection number
"#
$choosePath = #"
Enter path to file..
Notes
- Filename:
If file is located in script directory ($pwd) you can enter the filename without a path e.g. users.txt
- No quotation marks:
DO NOT put any quotes around the path even if it contains spaces e.g. e:\temp\new folder\users.txt
Enter path or filename
"#
$enterUsernames = #"
Enter username(s) seperate each with a comma e.g. test1 or test1,test2,test3`n`nEnter name(s)
"#
cls
:choose switch (Read-Host $chooseInputMethod) {
1 { cls; $usersFromScreen = Read-Host $enterUsernames }
2 {
cls;
$usersFromFile = Read-Host $choosePath;
if (-not (Test-Path $usersFromFile -PathType Leaf)) {
break choose
}
}
default { cls; $usersFromScreen = Read-Host $enterUsernames }
}
Write-Host "hello"
From the documentation for break:
In PowerShell, only loop keywords, such as Foreach, For, and While can have a label.
So, switch, even though it has looping capabilities, is not considered a loop in this case.
However, in this case I don't see why break without a label would not suffice.
A break to the switch will exit it. There's only one loop (switch, which works on arrays), so there's no difference between a plain break and breaking to the switch. You seem to want the switch inside another loop to repeat it.
# only runs once
:outer while (1) {
'outer'
while (1) {
'inner'
break outer
}
}
outer
inner
Demo of switch with a label and looping. The documentation isn't perfect.
# only runs once
:outer switch (1..10) {
default {
'outer'
foreach ($i in 1..10) {
'inner'
break outer
}
}
}
outer
inner
Another switch demo.
switch (1..4) {
{ $_ % 2 } { "$_ is odd" }
default { "$_ is even" }
}
1 is odd
2 is even
3 is odd
4 is even

Comparing AD sites and SCCM boundaries

I m trying to compare the Active directory sites with SCCM boundaries, by using the below powershell scripts, but its not giving the output as expected.
There are 3 AD sites actually available in SCCM, however the script gives me an output that there is no AD sites available in SCCM boundaries.
$sites = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites
$CMBoundary = Get-CMBoundary | select value
foreach ($adsite in $sites.name) {
foreach ($cmb in $CMBoundary.value ) {
if (($cmb | select value) -eq ($adsite | select name)) {
"$adsite available in CM"
}
else { "$adsite is NOT in CM $cmb" }
}
}
Could someone please help me on this.
Think I see the issue.
On line 3, you're selecting the .Name property from $sites and storing it in $adsite, for each step of your loop.
foreach ($adsite in $sites.name) {
However, you then also attempt to take the .Name property again, with this line:
if (($cmb | select value) -eq ($adsite | select name))
This won't work. You've set $adsite to be equal to whatever was in $sites.Name, but that doesn't give a .Name property to $adsite.
Try this again, this time with the second Select statement removed. The reason this was failing is that there wouldn't be anything to compare against. I've revised your code to remove this logic, let me know if it works.
foreach ($adsite in $sites.name) {
foreach ($cmb in $CMBoundary.value ) {
if ($cmb -eq $adsite) {
"$adsite available in CM"
}
else { "$adsite is NOT in CM $cmb" }
}
}
Be warned, however, that if your boundary name doesn't exactly match an AD Site name, that this code will give you a lot of false positives.

How can I quickly find VMs with serial ports in PowerCLI

I have a script that takes about 15 minutes to run, checking various aspects of ~700 VMs. This isn't a problem, but I now want to find devices that have serial ports attached. This is a function I added to check for this:
Function UsbSerialCheck ($vm)
{
$ProbDevices = #()
$devices = $vm.ExtensionData.Config.Hardware.Device
foreach($device in $devices)
{
$devType = $device.GetType().Name
if($devType -eq "VirtualSerialPort")
{
$ProbDevices += $device.DeviceInfo.Label
}
}
$global:USBSerialLookup = [string]::join("/",$ProbDevices)
}
Adding this function adds an hour to the length of time the script runs, which is not acceptable. Is it possible to do this in a more efficient way? All ways I've discovered are variants of this.
Also, I am aware that using global variables in the way shown above is not ideal. I would prefer not to do this; however, I am adding onto an existing script, and using their style/formatting.
Appending to arrays ($arr += $newItem) in a loop doesn't perform well, because it copies all existing elements to a new array. This should provide better performance:
$ProbDevices = $vm.ExtensionData.Config.Hardware.Device `
| ? { $_.GetType().Name -eq 'VirtualSerialPort' } `
| % { $_.DeviceInfo.Label }