Powershell For Each Variable By Number - powershell

I have the following variables:
$testvar_1 = 1, 100
$testvar_2 = 1, 20
$testvar_3 = 0, 260
$testvar_4 = 1, 10
How can I make a for loop that will loop through each variable array?
* When you dont know the total number of $testvar_n variables and it changes constantly. *
Something like For Each $testvar_n do { $testvar_n[1] = $testvar_n[1] + 10 }
Sorry very new to powershell.
EDIT:
Perhaps something that can work like the below?
get-variable testvar* | ForEach-Object -Process {$_[1]+10}

You can do something like the following if you need to actually update the existing variables.
foreach ($var in (Get-Variable -Name testvar_[0-9]* -ValueOnly)) {
$var[1] += 10
}
Note: [0-9] is the wildcard range operator for any single digit. The wildcard character * matches zero or more characters, which could match testvar_1abc for example. You can further prevent that with a Where-Object condition if necessary. See below for further filtering.
$vars = Get-Variable -Name testvar_[0-9]* | Where Name -match '^testvar_\d+$'
foreach ($var in $vars) {
$var.Value[1] += 10
}

Related

Windows PowerShell: How to parse the log file?

I have an input file with below contents:
27/08/2020 02:47:37.365 (-0516) hostname12 ult_licesrv ULT 5 LiceSrv Main[108 00000 Session 'session1' (from 'vmpms1\app1#pmc21app20.pm.com') request for 1 additional licenses for module 'SA-XT' - 1 licenses have been allocated by concurrent usage category 'Unlimited' (session module usage now 1, session category usage now 1, total module concurrent usage now 1, total category usage now 1)
27/08/2020 02:47:37.600 (-0516) hostname13 ult_licesrv ULT 5 LiceSrv Main[108 00000 Session 'sssion2' (from 'vmpms2\app1#pmc21app20.pm.com') request for 1 additional licenses for module 'SA-XT-Read' - 1 licenses have been allocated by concurrent usage category 'Floating' (session module usage now 2, session category usage now 2, total module concurrent usage now 1, total category usage now 1)
27/08/2020 02:47:37.115 (-0516) hostname141 ult_licesrv CMN 5 Logging Housekee 00000 Deleting old log file 'C:\Program Files\PMCOM Global\License Server\diag_ult_licesrv_20200824_011130.log.gz' as it exceeds the purge threashold of 72 hours
27/08/2020 02:47:37.115 (-0516) hostname141 ult_licesrv CMN 5 Logging Housekee 00000 Deleting old log file 'C:\Program Files\PMCOM Global\License Server\diag_ult_licesrv_20200824_021310.log.gz' as it exceeds the purge threashold of 72 hours
27/08/2020 02:47:37.625 (-0516) hostname150 ult_licesrv ULT 5 LiceSrv Main[108 00000 Session 'session1' (from 'vmpms1\app1#pmc21app20.pm.com') request for 1 additional licenses for module 'SA-XT' - 1 licenses have been allocated by concurrent usage category 'Unlimited' (session module usage now 2, session category usage now 1, total module concurrent usage now 2, total category usage now 1)
I need to generate and output file like below:
Date,time,hostname,session_module_usage,session_category_usage,module_concurrent_usage,total_category_usage
27/08/2020,02:47:37.365 (-0516),hostname12,1,1,1,1
27/08/2020,02:47:37.600 (-0516),hostname13,2,2,1,1
27/08/2020,02:47:37.115 (-0516),hostname141,0,0,0,0
27/08/2020,02:47:37.115 (-0516),hostname141,0,0,0,0
27/08/2020,02:47:37.625 (-0516),hostname150,2,1,2,1
The output data order is: Date,time,hostname,session_module_usage,session_category_usage,module_concurrent_usage,total_category_usage.
Put 0,0,0,0 if no entry for session_module_usage,session_category_usage,module_concurrent_usage,total_category_usage
I need to get content from the input file and write the output to another file.
Update
I have created a file input.txt in F drive and pasted the log details into it.
Then I form an array by splitting the file content when a new line occurs like below.
$myList = (Get-Content -Path F:\input.txt) -split '\n'
Now I got 5 items in my array myList. Then I replace the multiple blank spaces with a single blank space and formed a new array by splitting each element by blank space. Then I print the 0 to 3 array elements. Now I need to add the end values (session_module_usage,session_category_usage,module_concurrent_usage,total_category_usage).
PS C:\Users\user> $myList = (Get-Content -Path F:\input.txt) -split '\n'
PS C:\Users\user> $myList.Length
5
PS C:\Users\user> $myList = (Get-Content -Path F:\input.txt) -split '\n'
PS C:\Users\user> $myList.Length
5
PS C:\Users\user> for ($i = 0; $i -le ($myList.length - 1); $i += 1) {
>> $newList = ($myList[$i] -replace '\s+', ' ') -split ' '
>> $newList[0]+','+$newList[1]+' '+$newList[2]+','+$newList[3]
>> }
27/08/2020,02:47:37.365 (-0516),hostname12
27/08/2020,02:47:37.600 (-0516),hostname13
27/08/2020,02:47:37.115 (-0516),hostname141
27/08/2020,02:47:37.115 (-0516),hostname141
27/08/2020,02:47:37.625 (-0516),hostname150
If you really need to filter on the granularity that you're looking for, then you may need to use regex to filter the lines.
This would assume that the rows have similarly labeled lines before the values you're looking for, so keep that in mind.
[System.Collections.ArrayList]$filteredRows = #()
$log = Get-Content -Path C:\logfile.log
foreach ($row in $log) {
$rowIndex = $log.IndexOf($row)
$date = ([regex]::Match($log[$rowIndex],'^\d+\/\d+\/\d+')).value
$time = ([regex]::Match($log[$rowIndex],'\d+:\d+:\d+\.\d+\s\(\S+\)')).value
$hostname = ([regex]::Match($log[$rowIndex],'(?<=\d\d\d\d\) )\w+')).value
$sessionModuleUsage = ([regex]::Match($log[$rowIndex],'(?<=session module usage now )\d')).value
if (!$sessionModuleUsage) {
$sessionModuleUsage = 0
}
$sessionCategoryUsage = ([regex]::Match($log[$rowIndex],'(?<=session category usage now )\d')).value
if (!$sessionCategoryUsage) {
$sessionCategoryUsage = 0
}
$moduleConcurrentUsage = ([regex]::Match($log[$rowIndex],'(?<=total module concurrent usage now )\d')).value
if (!$moduleConcurrentUsage) {
$moduleConcurrentUsage = 0
}
$totalCategoryUsage = ([regex]::Match($log[$rowIndex],'(?<=total category usage now )\d')).value
if (!$totalCategoryUsage) {
$totalCategoryUsage = 0
}
$hash = [ordered]#{
Date = $date
time = $time
hostname = $hostname
session_module_usage = $sessionModuleUsage
session_category_usage = $sessionCategoryUsage
module_concurrent_usage = $moduleConcurrentUsage
total_category_usage = $totalCategoryUsage
}
$rowData = New-Object -TypeName 'psobject' -Property $hash
$filteredRows.Add($rowData) > $null
}
$csv = $filteredRows | convertto-csv -NoTypeInformation -Delimiter "," | foreach {$_ -replace '"',''}
$csv | Out-File C:\results.csv
What essentially needs to happen is that we need to get-content of the log, which returns an array with each item terminated on a newline.
Once we have the rows, we need to grab the values via regex
Since you want zeroes in some of the items if those values don't exist, I have if statements that assign '0' if the regex returns nothing
Finally, we add each filtered item to a PSObject and append that object to an array of objects in each iteration.
Then export to a CSV.
You can probably pick apart the lines with a regex and substrings easily enough. Basically something like the following:
# Iterate over the lines of the input file
Get-Content F:\input.txt |
ForEach-Object {
# Extract the individual fields
$Date = $_.Substring(0, 10)
$Time = $_.Substring(12, $_.IndexOf(')') - 11)
$Hostname = $_.Substring(34, $_.IndexOf(' ', 34) - 34)
$session_module_usage = 0
$session_category_usage = 0
$module_concurrent_usage = 0
$total_category_usage = 0
if ($_ -match 'session module usage now (\d+), session category usage now (\d+), total module concurrent usage now (\d+), total category usage now (\d+)') {
$session_module_usage = $Matches[1]
$session_category_usage = $Matches[2]
$module_concurrent_usage = $Matches[3]
$total_category_usage = $Matches[4]
}
# Create custom object with those properties
New-Object PSObject -Property #{
Date = $Date
time = $Time
hostname = $Hostname
session_module_usage = $session_module_usage
session_category_usage = $session_category_usage
module_concurrent_usage = $module_concurrent_usage
total_category_usage = $total_category_usage
}
} |
# Ensure column order in output
Select-Object Date,time,hostname,session_module_usage,session_category_usage,module_concurrent_usage,total_category_usage |
# Write as CSV - without quotes
ConvertTo-Csv -NoTypeInformation |
ForEach-Object { $_ -replace '"' } |
Out-File F:\output.csv
Whether to pull the date, time, and host name from the line with substrings or regex is probably a matter of taste. Same goes for how strict the format must be matched, but that to me mostly depends on how rigid the format is. For more free-form things where different lines would match different regexes, or multiple lines makes up a single record, I also quite like switch -Regex to iterate over the lines.

Concatenating a variable and a string literal without a space to an array using PowerShell

I'm trying add to a variable and a string in an array dynamically but i'm not getting expected output.
(1) I'm getting env name
(2) Concatinating the string and variable in an array
Code is as follows.
$env = $env:COMPUTERNAME.Substring(0,2)
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
$serverCount = $servers -split(",") | measure | % { $_.Count }
For ($i=0; $i -lt $serverCount; $i++)
{
$ServerName = $servers -split(',') -replace '\[\d+\]'
$server = $ServerName[$i]
Write-Host $server
}
output i'm getting as
$env+"server1.test.com"
$env+"server2.test.com"
Values are not getting concatenated properly and variable value is not getting displayed. Any help.
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
This is a scriptblock, not an array. {} is like a function, you have to run it for it to do anything (such as evaluating $env).
When you force it into a string using -split(",") what you get is text representation of the source code in the scriptblock, including the variable names.
As #Olaf comments, the right way to create an array of names is
$servers = ($env + "server1.test.com"), ($env + "server2.test.com")
This might be how I'd write it:
$env = $env:COMPUTERNAME.Substring(0,2)
"server1.test.com", "server2.test.com" | foreach-object {
"$env$_" -replace '\d+'
}

Passing a path directory to a function in PowerShell

I am new to PS and I am trying to write a function which takes in parameters from a global variable. I want to pass a path name read from a .txt file into a function in the same script.
function GetCorrectChildren ([string] $homepath,$min,$max,$row)
{
#Testpoint 2
write-host "homepath = $homepath"
$ColItem = (Get-ChildItem $homepath |? {$_.PSIsContainer} | sort-object)
}
foreach ($homepath in (Get-Content $PSScriptRoot\homepath_short.txt))
{
$freeSpace = [win32api]::GetDiskFreeSpace("$homepath").FreeBytesAvailable / 1073741824
$totalSpace = [win32api]::GetDiskFreeSpace("$homepath").TotalNumberOfBytes / 1073741824
$percentageFreeSpace = $freeSpace / $totalSpace * 100
if($freeSpace -lt $threshold)
{
#Testpoint 1
write-host "homepath = $homepath"
GetCorrectChildren ("$homepath",$min,$max,$OriRow)
}
For #Testpoint 1, it returns the path name correctly which is \\C:\test1\test_a. However in #Testpoint 2 it returns \\C:\test1\test_a 20 30 System.Object.
I don't understand what does the 20 30 System.Object mean and where does it come from? Can some one shine some light on this? Thanks
Change the last line
GetCorrectChildren ("$homepath",$min,$max,$OriRow)
to
GetCorrectChildren $homepath $min $max $OriRow
as ("$homepath",$min,$max,$OriRow) creates a single array with the four values and passes it to the function GetCorrectChildren as its first parameter so that write-host "homepath = $homepath" in it will print all 4 values

Powershell adding numbers in a loop

I have the below code which is meant to total up the time offset as the loop rotates (I will then need to divide this by 10 to get the average but first I need to get this bit working).
I'm assuming I need to cast something as [INT] but I've tried multiple locations that would make sense to no avail. I just end up with O's.
$winTimeStripchart = w32tm /stripchart /computer:0.pool.ntp.org /dataonly /samples:10
$WinTimeOffset = $null
For($i=3; $i -le 12; $i++){
$Offset = $winTimeStripchart[$i].split("-")
$trimmedOffset = $Offset[1].trim("s")
$winTimeOffset = $winTimeOffset + $trimmedOffset
}
Write-Host "Total: $winTimeOffset"
# Now need to divide by 10.
sample data:
20:30:23, -00.0698082s
20:30:25, -00.0704645s
20:30:27, -00.0708694s
20:30:29, -00.0728990s
20:30:31, -00.0719226s
20:30:33, -00.0749031s
20:30:36, -00.0778656s
20:30:38, -00.0782183s
20:30:40, -00.0752974s
20:30:42, -00.0760958s
You can try this one line command :
$a = w32tm /stripchart /computer:0.fr.pool.ntp.org /dataonly /samples:10 | select -Skip 3 | % {[double]$_.Substring(11,10)} | Measure-Object -Average -sum
$a can also give you maximum and minimum adding Measure-Object params.
You'll want to cast to a double rather than int. The following should do it:
[System.Collections.ArrayList]$winTimeStripchart = w32tm /stripchart /computer:0.pool.ntp.org /dataonly /samples:10
$WinTimeOffset = $null
$winTimeStripchart.RemoveRange(0,3)
foreach($entry in $winTimeStripchart){
$Offset = $entry.split("-")
$trimmedOffset = $Offset[1].trim("s")
$winTimeOffset = [double]$winTimeOffset + [double]$trimmedOffset
}
Write-Host "Total: $winTimeOffset"
Write-Host "Average: $($winTimeOffset/$winTimeStripchart.count)"
Sample output:
Total: 6.1581437
Average: 0.61581437
I've make some other tweaks to the script as well to make it more scalable:
Foreach rather than a for loop
Using a list rather than array and stripping first 3 entries.
Dividing buy number of entries in list.
regards
Arcas

PowerShell Compare-Object, results in context

I'm using Compare-Object in PowerShell to compare two XML files. It adequately displays the differences between the two using <= and =>. My problem is that I want to see the difference in context. Since it's XML, one line, one node, is different, but I don't know where that lives in the overall document. If I could grab say 5 lines before and 3 lines after it, it would give me enough information to understand what it is in context. Any ideas?
You can start from something like this:
$a = gc a.xml
$b = gc b.xml
if ($a.Length -ne $b.Length)
{ "File lenght is different" }
else
{
for ( $i= 0; $i -le $a.Length; $i++)
{
If ( $a[$i] -notmatch $b[$i] )
{
#for more context change the range i.e.: -2..2
-1..1 | % { "Line number {0}: value in file a is {1} - value in file b {2}" -f ($i+$_),$a[$i+$_], $b[$i+$_] }
" "
}
}
}
Compare-Object comes with an IncludeEqual parameter that might give what you are looking for:
[xml]$aa = "<this>
<they>1</they>
<they>2></they>
</this>"
[xml]$bb = "<this>
<they>1</they>
<they>2</they>
</this>"
Compare-Object $aa.this.they $bb.this.they -IncludeEqual
Result
InputObject SideIndicator
----------- -------------
1 ==
2 =>
2> <=