Powershell - how can I make - powershell

Trying to find the numbers in my file divisible by 3. How can I make my for each loop read each number individually?
this is my file:
6 9 7
-----
5 2 9
3 4 4
1 6 9
This is my code so far:
function number{
param($b)
# Loop through all lines of input
foreach($a in $b){
if ($line = % 3)
{
Write-Output "divisible by 3"
}
else {
Write-Output "number not divisible by 3"
}
}
}
#Variables
#Get input from csv file
$a = Import-Csv "document 2.Dat"

How have you got this far in, without realising that none of that even does anything at all? Whatever development approach you're using, you need to rethink it.
Hints that something is wrong:
How is it printing more dashes than there even are in the file? What is it actually printing? Use useful debugging/testing tool, wrap each thing in dashes so you can see where they start and end:
Oh that's broken.
Inside function number { put write-host 'hello?' and see that it's never printing anything.
Try calling the function by hand to see what it does:
Oh I have no idea what number is not divisible by 3, I'd better fix that so I can see what's going on.
And if you have an eye looking for details
where does $line get assigned? What is = doing in an if test? What is % 3 doing with nothing to the left of the %? Why am I using variable names like $a and $b which don't help me follow what's happening at all?
and, of course, "*why am I not write-host "..." all the way through, and/or stepping through this code in the debugger to see what's happening?
Google(*) "read file powershell"
Try it
That's my file alright. And the limits of the output are ... lines. Cool.
function number I should give it a better name but.
Sigh. alright, alright.
No output, even from a simple 'hi'? Ah, call the function.
Great.
Pass a parameter to it and print it...
No output.
Enough screenshots.
Pass a parameter when calling the function. Get-NumbersWhichDivideEvenlyByThree $FileContent
Iterate over the lines and print them inside the function.
Google "powershell get numbers from string" and stuff
Iteratively develop your code, going from working block to working block. Never end up in a position where you have a dozen lines that all don't work in half a dozen different ways all at once, and nowhere to go from there.
Bit you actually asked
Get numbers out of a string.
Use regex. This is exactly why they exist. But to try and keep it simple - in a way that's actually more complicated but tough - break the lines apart on spaces, and pick out the pieces which are numbers and throw the rest away.
To get this with a reasonably nice answer, you almost need to just magically know about -split, perhaps by stumbling on one of #mklement0's answers here about unary split or split has an unary form or the unary form of the -split operator is key here , or, I guess, have read help about_Split in careful detail.
-split '6 9 7' # this splits the line into parts on *runs* of whitespace
6
9
7 # look like numbers, but are strings really
So you get some text pieces, including the line of ----- in the file, that will be among them. And you need to test which are numbers and keep them, and which are dashes (letters, punctuation, etc) and throw those away.
$thing -as [int] # will try to cast $thing as a (whole) number, and silently fail (no exception) if it cannot.
# Split the line into pieces. Try to convert each piece to a number.
# Filter out the ones which weren't numbers and failed to convert.
$pieces = -split $line
$pieces = $pieces | ForEach-Object { $_ -as [int] }
$numbers = $pieces | Where-Object { $_ -ne $null }
Then you can do the % 3 test. And have code like:
function Get-NumbersWhichDivideEvenlyByThree {
param($lines)
foreach ($line in $lines)
{
$pieces = -split $line
$pieces = $pieces | ForEach-Object { $_ -as [int] }
$numbers = $pieces | Where-Object { $_ -ne $null }
foreach ($number in $numbers)
{
if (0 -eq $number % 3)
{
Write-Output "$number divisible by 3"
}
else
{
Write-Output "$number not divisible by 3"
}
}
}
}
$FileContent = Get-Content 'D:\document 2.dat'
Get-NumbersWhichDivideEvenlyByThree $FileContent
and output like:
(-split(gc D:\test.txt -Raw)-match'\d+')|%{"$_$(('',' not')[$_%3]) divisible by 3"}

Related

From output, include line only contain a key word and extract first field from the included lines with powershell

With PowerShell, I am trying to extract the first field from an output that contains multiple lines as below. Along with this, I wanted to exclude if the line doesn't have a key 'web:'
Getting apps in org SomeOrg / space Somespace as x-user...
name requested state processes routes
maskedappname1 started web:1/1 maskedappname1.com
maskedappname2 started web:0/1 maskedappname2.com
maskedappname3 started web:1/1 maskedappname3.com
maskedappname4 started web:1/1 maskedappname4.com
maskedappname5 started web:1/1 maskedappname5.com
maskedappname6 stopped web:0/1 maskedappname6.com
after execution, my final output should be
maskedappname1
maskedappname2
maskedappname3
maskedappname4
maskedappname5
maskedappname6
tried multiple ways didn't help me.
Much appreciate it if I get some help on this.
Thanks.
You can use a switch with the -Regex parameter to match any line having web: and capture the everything from the beginning of the line until the first whitespace.
switch -File path\to\file.ext -Regex {
'(^\S+).+web:' { $Matches[1] }
}
See https://regex101.com/r/fxQtcN/1 for details.
iterate through each line
$array = $textWithMultipleLines.Split(“`n”)
foreach ($line in $array){
# ...
}
take fixed length (if possible) or split on space ant take the first item of the split array
($extract -split " ")[0]
# or the regex way:
$extract -replace '^([^ ]+ ).+$','$1'
all together
$array = $textWithMultipleLines.Split(“`n”)
foreach ($line in $array){
$maskedAppName = ($line -split " ")[0]
Write-Host "maskedAppName: $maskedAppName"
}

How to get 2 variables from a file PowerShell

I'm trying to make an script that take numbers from a file and then do some maths with them, but I don't know how to take from a single line 2 variables.
The file it must be something like this:
3 5
What I need is that one variable is for example $a be 3 and other $b be 5
$a=3
$b=5
The problem is that I found this
$Contents = Get-Content ".\file.txt"
$a = $Contents[0] -split(" ")[1]
$b = $Contents[1] -split(" ")[1]
but it doesen´t work with the second number, how can I do this?
To refer to the first line in the file, you want $Contents[0] ($Contents[1] would refer to the second line).
$a,$b = -split $Contents[0] -as [int[]]
Using -split in unary mode will make PowerShell split on any sequence of consecutive whitespace characters, and throw away any empty parts (this way it works when the iput has leading or trailing whitespace, like " 3 5 ").
The -as [int[]] operation will force PowerShell to attempt to convert the resulting string values to [int] values, so now you can meaningfully do integer arithmetic with them:
PS ~> $a + $b
8

Counting in powershell?

Can someone explain to me whats going on here? Its a piece of code i got from a script we use here at work and i believe that i understand why it counts but, from there im lost. Any generalization on why/how it does so would be greatly appreciated.
Please note, i did search everywhere before asking on here.
$gc = Get-ChildItem C:\users | Select-Object -ExpandProperty Name
$ls = #($gc)
$gcls = $ls.count
For($i=0; $I -lt $gcls; $i++){
Write-host "$($i): $($ls[$i])"
}
$selection = Read-Host "Enter Number"
$selection = $selection -split " "
$gc[$selection]
gc is self explanatory.
ls is as well throwing the output into an array
gcls is creating the variable to the list of counted strings
I kinda understand whats going on in the for statement where its setting $i to 0, saying if $i -lt the counted strings in $gcls (which it is due to $i=0), and it is counting the output. Now im still kind of following but, I just don't seem to understand how its outputting the strings the way it is.
Anyone familiar with this?
Lee_Dailey also answered this above as a comment.
Inlined comments explaining what each line does and where the count comes from, how the write-host works, etc.
$gc = Get-ChildItem C:\users | Select-Object -ExpandProperty Name #gets all items in c:\users
$ls = #($gc) #this seems redundant to me, but, puts output from get-childitem above into $ls
$gcls = $ls.count #stores a count of items found in get-childitem in $gcls
For($i=0; $I -lt $gcls; $i++){
<#
check out https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_for?view=powershell-7.1
specifically:
The For statement (also known as a For loop) is a language construct you can use to create a loop
that runs commands in a command block while a specified condition evaluates to $true.
So this will run the statement in the scriptblock (Write-host) while $i is less than $gcls (the count of items found in get-childitem).
each time it loops, it willll print $($i): $($ls[$i]) to the console and then increase $i by 1 (the $i++ in the For)
breaking down the print statement:
$($i) - prints the current loop count. The $() is a subexpression operator. It isnt really needed here, but it isnt hurting anything see https://ss64.com/ps/syntax-operators.html
$($ls[$i]) - we have a subexpression operator again. This time were printing a value in the variable $ls. The [$i] gets an item from the array. We need the $(), otherwise it would print all the contents of $ls rather than just the one item we wanted - try it yourself write-host "$($ls[0])" vs write-host "$ls[0]"
$ls[0] would get the first item in the array
$ls[1] would get the second, so on and so forth. Can see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_arrays?view=powershell-7.1 for more info
#>
Write-host "$($i): $($ls[$i])"
}
$selection = Read-Host "Enter Number" #prompts the user for input, expects INT seperated by spaces - 5 10
$selection = $selection -split " " #splits the user input
$gc[$selection] #prints the names using user input above. If the user enters 5, theyd get the 5th item returned by Get-ChildItem. Entering "5 10" would get the 5th and the 10th item. Again, see about_arrays above

Powershell - Is there a benefit to using the Peek method over Readline (while not null)?

I've been working on a PowerShell script using Streamreader (and StreamWriter) to parse a large file into smaller reports. While doing some searching around on the best way to put this thing together, I've found there are two methods largely used to read content to the end of the file.
1 - while ($reader.Peek() -ge 0) { $line = $reader.Readline() ... }
2 - while (($line = $read.ReadLine()) -ne $null) { do stuff ... }
From the documentation, it looks like Peek will read the next value, but not change the position of the reader. It looks like ReadLine will essentially do the same, but read a whole string/line. I feel like this is a "no-duh" question - is it really more efficient to actually peek at a value before reading the line, or is it just an extra step before assigning the reader to a variable?
Thank you in advance!
Since you need lines anyway, I see no reason to Peek(). If you really want to check whether you're at the end, then the .EndOfStream property is likely to be more accurate anyway.
As discussed here, .Peek() can return -1 when errors occur as well, not just when the end of stream is reached. Most answers there also recommend avoiding it and just using .ReadLine().
mklement0 also mentioned using System.IO.File.ReadLines. This returns an enumerable so you can just call it with a path and use it like other enumerables, without loading all the lines at once (so it still works with large files).
You could use it with foreach or with ForEach-Object, for example:
foreach ($line in ([System.IO.File]::ReadLines('path\to\file'))) {
$line
}
[System.IO.File]::ReadLines('path\to\file') | ForEach-Object -Process {
$_
}
$reader = [System.IO.File]::ReadLines('path\to\file')
foreach ($line in $reader) { $line }
$reader | ForEach-Object -Process { $_ }

Powershell being too clever

Apologies for what is probably a newbish question.
I am writing some Powershell scripts that run various queries against AD. They will usually return a bunch of results, which are easy to deal with, ie:
$results = myQuery
write-host "Returned " + $results.Count + " results."
foreach ($result in $results) { doSomething }
No worries. However, if there is only 1 result, Powershell automagically translates that result into a single object, rather than an array that contains 1 object. So the above code would break, both at the Count and the foreach. I'm sure the same problem would occur with 0 results.
Could someone suggest an elegant way to handle this? Perhaps some way to cast the results so they are always an array?
Change the first line of code to
$results = #(myQuery)
This will always return an array. See this blog entry for additional details.
Actually, the foreach works just fine. All uses of foreach (the foreach keyword, the Foreach-Object cmdlet, and Foreach-Object's aliases "foreach" and "%") all have the same behavior of "wrapping" the object in question in an array if needed. Thus, all uses of foreach will work with both scalar and array values.
Annoyingly, this means they work with null values too. Say I do:
$x = $null
foreach ($y in $x) {Write-Host "Hello world 1"}
$x | Foreach-Object {Write-Host "Hello world 2"}
I'll get
"Hello world 1"
"Hello world 2"
out of that.
This has bitten me as well. No clever ideas on how to fix $results.Count, but the foreach can be fixed by switching to a pipeline.
$scalar = 1
$list = (1,2)
$list | % { $_ }
prints
1
2
$scalar | % { $_ }
prints
1