Operator -gt and -le / Problem with negative numbers - powershell

i have the following code snippet where i change the values in a column (named G) of a csv to Y if the integer value is greater then 1 and to N if it is equal to 1 and smaller.
ForEach-Object {if ($_.G -gt '1') {$_.G = 'Y'} if ($_.G -le '1') {$_.G = 'N'} $_}
It works fine with the exception of negative numbers. I always get a Y. I don't have any idea. Example data:
F,G
item1, -58
item2, -77
item3, 562
Does anyone have an idea?
Regards, Hubertus

In order to evaluate the $_.G property as a number you need to specify the type as [int]. Example using your code:
$testObject = New-Object PSObject -Property #{
G='-1'
}
$testObject| %{
if ([int]$_.G -gt 1)
{
$out = "{0} is greater than 1" -f $_.G
Write-Host $out -ForegroundColor Green
[string]$_.G = "Y"
}
elseif ([int]$_.G -le 1)
{
$out = "{0} is Less than 1" -f $_.G
Write-Host $out -ForegroundColor Green
[string]$_.G = "N"
}
}
Note: In order to assign $_.G as a string you have to change the type to [string]. In my opinion, I would use another property to indicate "Y/N" instead of flipping the type back and forth on the property.

The left side of -le or -gt controls the type for both sides, int32 (integer) in this case. You probably want an else in there, to not look at the G again after changing it.
'G
-1
1
2' |
convertfrom-csv |
ForEach-Object {
if (1 -le $_.G)
{$_.G = 'Y'}
else
{$_.G = 'N'}
$_
}
G
-
N
Y
Y

Related

Check if value falls within a specific range powershell

I am having a code which is giving me 2 values like below
$pattern = '\s(-?\d+.?\d+)\s'
$RX_Val = [regex]::Matches($RXTX_Data[0], $pattern).Value
$TX_Val = [regex]::Matches($RXTX_Data[1], $pattern).Value
PS C:\Windows\system32> $RX_Val
-3.4
PS C:\Windows\system32> $TX_Val
-2.3
Need get RX and TX value should fall under range -1 to -7
like the above 2 values falls within the range
if the values are like 1 and -8 respecively, then it should give error
I tried below code, but not getting the proper response
if((($RX_Val -gt -1) -and ($RX_Val -lt -7)))# -and (($TX_Val -gt '-1') -and ($TX_Val -lt '-7')))
{
Write-Host "OK"
}
else
{
Write-Host "NOT OK"
}
also tried
$RX_Val -In -1..-7
please let me know what i am missing here
Tried given solution in below way
$RX_Val = [int][regex]::Matches($RXTX_Data[0], $pattern).Value
$TX_Val = [int][regex]::Matches($RXTX_Data[1], $pattern).Value
if(($RX_Val -in -1..-7) -and ($TX_Val -in -1..-7))
{
Write-Host "OK"
}
else
{
$RXTX_Data | Out-File -FilePath "E:\$file_name" -Force
}
but failed for below scenario as the values are converted to int. it is suppose to print OK
$RX_Val=-0.1
$TX_Val=-1.5
The issue is that the value return by your regex extraction is a STRING. You need to convert it to an INT to be able to do your calculation.
$RX_Val = [int][regex]::Matches($RX, $pattern).Value
$TX_Val = [int][regex]::Matches($TX, $pattern).Value
Sure you can make the logic work from there.
As a bonus, the cast to [int] will also take care of the whitespace left from the regex.

Match Unique Value in Column and put all values on that row in to variables

I'm working with a CSV with 5 Columns, One of the Columns has unique Values.
Fruit, Number, Car, item, color
apple, 2, Chevy, ball, blue
apple, 1, Ford, ball, green
orange, 3, Ford, string, "red,green"
orange, 5, Mazda, key, red
Banana, 4, Tesla, desk, yellow
I need to search for 3 and have it return orange ford string "red,green" as their own variable
i.e. $fruit1 becomes orange $car1 becomes ford $item becomes string and $color bcomes red,green
I can do the search and have it tell me it found 3, but it still just puts runs everything through $fruit1 and if I tell it to write $fruit1 to a file it just get a repeating mess
I Need to Get output to a TXT file like so
for #3
FRUIT=orange
Car=Ford
ITEM = string
COLOR ="red,green"
whith each value in a different part of the file/newline
I can't post from the machine the script is on. So values changed to match my example
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
LogWrite "Started execution of script.ps1"
$masterlist = Import-Csv ($filepath + "\" + "masterlistfile.csv" )
$FruitName = #()
$NumberName = #()
$Carname = #()
$ItemName = #()
$Colorname = #()
$masterlist |ForEach-Object {
$FruitName += $_.fruit
$NumberName += $_.number
$Carname += $_.car
$Itemname += $_.item
$Colorname += $_.color
}
$number = 3
$FruitIdentified
$CarIdentified
$ItemIdentified
$ColorIdentified
LogWrite "NUmber $number to be searched in masterlistfile "
if ($NumberName -eq $number)
{
LogWrite "Number found in the list..."
$Where = [array]::IndexOf($NumberName, $number)
LogWrite "Fruit Name : $FruitrName[$Where] "
$FruitIdentified = $FruitName[$Where]
$CarIdentified = $CarName[$Where]
$ItemIdentified = $ItemName[$Where]
}
You can use the following to read your CSV and then export the result with your expected output:
$number = 3
Import-Csv path/to/csv.csv | ForEach-Object {
if($_.number -eq $number) {
"for #$number"
foreach($prop in $_.PSObject.Properties.Name -ne 'Number') {
'{0}={2}{1}{2}' -f $prop, $_.$prop, ($null, '"')[$prop -eq 'color']
}
}
} | Set-Content path/to/file.ext
Note that Set-Content will overwrite the export file, if you want to append you would use Add-Content as in your function.
To give some context on what the code does:
Read the CSV and convert it to an object with Import-Csv
Loop over all objects and filter where the value of the Number property is equal to $number.
Output for #$number, in this example would be for #3".
Get all properties of the object using PSObject.Properties.Name and exclude the Number property using -ne 'Number'.
Loop over the Property Names and output '{0}={1}' -f $prop, $_.$prop, here we use the Format Operator -f, {0} would be the Property Name and {1} would be the Property Value. {2} will wrap the value with ".." if the Property Name is color.
The output you would be getting using your CSV for input would be:
for #3
Fruit=orange
Car=Ford
item=string
color="red,green"

Why is this do/until loop exiting with values starting 1?

Built a menu system which for the most part works fine but I've hit a weird validation error and I'm scratching my head as to why this function is escaping when you answer with "11" (or indeed any number starting with 1)
function Get-MenuSelection {
$totalOptions = 2
do {
$input = Read-Host "[1 <-> $totalOptions | (Q)uit - FIRST]"
while (!$input) {
$input = Read-Host "[1 <-> $totalOptions | (Q)uit - LOOP]"
}
} until ($input -lt $totalOptions -or $input -eq $totalOptions -or $input -eq "q")
Write-Host "exiting"
}
Get-MenuSelection
Output I'm getting:
./wtf.ps1
[1 <-> 2 | (Q)uit - FIRST]:
[1 <-> 2 | (Q)uit - LOOP]:
[1 <-> 2 | (Q)uit - LOOP]: test
[1 <-> 2 | (Q)uit - FIRST]: 22
[1 <-> 2 | (Q)uit - FIRST]: 9090
[1 <-> 2 | (Q)uit - FIRST]: 11
exiting
I'm clearly doing something wrong but just can't figure out what.
Solution
For those reading this some time in the future, I ended up with this - I chose to drop the 'q' options since it was just over-complicating the logic. Thanks to #AdminofThings and #mklement0 for the input. Appreciated.
function Get-MenuSelection {
param (
$output
)
[int]$totalOptions = $output.Count
do {
try { [int]$answer = Read-Host "Options: [1 <-> $totalOptions]" }
catch { }
if ($answer -eq "0" -or $answer -gt $totalOptions) {
Write-Host "Invalid input detected. Ctrl+C to quit."
}
} while ($answer -gt $totalOptions -or !$answer)
$returnedAnswer = "menu_$answer"
return $returnedAnswer
}
Since $input is an automatic/reserved variable, your code will not execute as intended. $input will likely result in an empty value during retrieval.
If we theoretically assume that $input is replaced by something that is not reserved, then a corresponding issue here is $input is a string and $totaloptions is an int. When PowerShell is faced with a comparison operation and both sides of the comparison don't match types, it will attempt to convert the righthand side (RHS) type to match the lefthand side (LHS). To get around this, you need to either cast $input as an [int] or bring $totaloptions to the LHS.
until ([int]$input -lt $totalOptions -or $input -eq $totalOptions -or $input -eq "q")
# OR
until ($totalOptions -gt $input -or $input -eq $totalOptions -or $input -eq "q")
An example of your situation:
#Unexpected Outcome
> [string]11 -lt [int]2
True
#Expected Outcome
> [int]11 -lt [int]2
False
#Expected Outcome
> [int]2 -gt [string]11
False

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> <=