How to get 2 variables from a file PowerShell - 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

Related

Incrementing a number each time it replaces a string

So I working with a script that looks like this. It works just fine the only issue is that it is counting the line numbers and I just wanted it to replace num each time with 1 2 3 4 etc insted it is looking like this 5 20 25 etc and that is because it seems to be counting and incremnting $c for each line not each time it replaces the string num.
$c=0
(Get-Content C:\Users\H\Desktop\3.txt) |
Foreach-Object {$_ -replace "num", $((++$c))} |
Out-File C:\Users\H\Desktop\4.txt
Try this:
$c = [ref] 0
$text = Get-Content 'C:\Users\H\Desktop\3.txt' -Raw
[regex]::Replace( $text, 'num', { (++$c.Value) } ) |
Set-Content 'C:\Users\H\Desktop\4.txt'
# With PowerShell 6+ you could write:
# (Get-Content 'C:\Users\H\Desktop\3.txt' -Raw) -replace 'num', { (++$c.Value) } |
# Set-Content 'C:\Users\H\Desktop\4.txt'
Copy-pastable demo:
$text = #'
num foo num
bar num bar
num baz num
'#
$c = [ref] 0
[regex]::Replace( $text, 'num', { (++$c.Value) } )
# With PowerShell 6+ you could write:
# $text -replace 'num', { (++$c.Value) }
Output:
1 foo 2
bar 3 bar
4 baz 5
Explanation:
Use a reference type ([ref]) variable instead of a plain variable. The scriptblock passed to [regex]::Replace() or -replace (PS 6+ only) runs in a child scope, so it can't modify variables from the parent scope directly. You can modify the members of a reference type, so this is why the [ref] trick works. Instead of [ref] you could also use a [PSCustomObject] or a [Hashtable] (which are both reference types) with a counter member.
The parenthesis around the expression ++$c.Value are required to output the value of the expression, which doesn't produce output by default. You already had that, I'm just explaining it for other visitors.
Using Get-Content with parameter -Raw can be faster, because the file's content gets output as a single multiline string instead of splitting it up into one string per line, at the expense of requiring more memory.
As for what you have tried:
$_ -replace "num", $((++$c))
You are passing an expression instead of a script block as the RHS argument to the -replace operator. Furthermore, a script block argument for the -replace operator is only supported beginning with PowerShell 6. For older versions you have to use the .NET function [Regex]::Replace.
This expression is evaluated once for each iteration of the ForEach-Object "loop", before the -replace operator is evaluated. So you are effectively just counting the lines of the file, not the number of occurences of the pattern.
Only a script block's execution can be delayed. It doesn't get called immediately in the place where you define it1, but when the function or operator that has a [ScriptBlock] parameter decides to call it, possibly multiple times as happens when the pattern has multiple matches. To define a script block, use {} as I did in my sample at the beginning.
[1] Unless you use the call or dot source operator, e.g. &{'foo'}

Add Content to a specific line in powershell

I have seen this post:
Add-Content - append to specific line
But I cannot add these lines because "Array index is out of range".
What my script is doing:
Find the line
Loop through the array that contains the data i want to add
$file[$line]+=$data
$line+=1
Write to file
Should I create a new file content and then add each line of the original file to it?
IF so, do you know how to do that and how to stop and add my data in between?
Here is the part of my code where I try to add:
$f=Get-Content $path
$ct=$begin+1 #$begin is the line where I want to place content under
foreach($add in $add_to_yaml)
{
$f[$ct]+=$add
$ct+=1
}
$f | Out-File -FilePath $file
Let's break down your script and try to analyze what's going on:
$f = Get-Content $path
Get-Content, by default, reads text files and spits out 1 string per individual line in the file. If the file found at $path has 10 lines, the resulting value stored in $f will be an array of 10 string values.
Worth noting is that array indices in PowerShell (and .NET in general) are zero-based - to get the 10th line from the file, we'd reference index 9 in the array ($f[9]).
That means that if you want to concatenate stuff to the end of (or "under") line 10, you need to specify index 9. For this reason, you'll want to change the following line:
$ct = $begin + 1 #$begin is the line where i want to place content under
to
$ct = $begin
Now that we have the correct starting offset, let's look at the loop:
foreach($add in $add_to_yaml)
{
$f[$ct] += $add
$ct += 1
}
Assuming $add_to_yaml contains multiple strings, the loop body will execute more than once. Let's take a look at the first statement:
$f[$ct] += $add
We know that $f[$ct] resolves to a string - and strings have the += operator overloaded to mean "string concatenation". That means that the string value stored in $f[$ct] will be modified (eg. the string will become longer), but the array $f itself does not change its size - it still contains the same number of strings, just one of them is a little longer.
Which brings us to the crux of your issue, this line right here:
$ct += 1
By incrementing the index counter, you effectively "skip" to the next string for every value in $add_to_yaml - so if the number of elements you want to add exceeds the number of lines after $begin, you naturally reach a point "beyond the bounds" of the array before you're finished.
Instead of incrementing $ct, make sure you concatenate your new string values with a newline sequence:
$f[$ct] = $f[$ct],$add -join [Environment]::Newline
Putting it all back together, you end up with something like this (notice we can discard $ct completely, since its value is constant an equal to $begin anyway):
$f = Get-Content $path
foreach($add in $add_to_yaml)
{
$f[$begin] = $f[$begin],$add -join [Environment]::Newline
}
But wait a minute - all the strings in $add_to_yaml are simply going to be joined by newlines - we can do that in a single -join operation and get rid of the loop too!
$f = Get-Content $path
$f[$begin] = #($f[$begin];$add_to_yaml) -join [Environment]::Newline
$f | Out-File -FilePath $file
Much simpler :)

Powershell split() function is returning more values than expected [duplicate]

This question already has answers here:
How to split string by string in Powershell
(6 answers)
Closed 5 years ago.
I have a csv file which conatins line something similar to this
"Some KIND of Test","[STATUS]","TESTNAME","VMNAME","TESTTME","SOME PARAMETER"
I am trying to parse this. I am using split to get STATUS and TESTNAME
$col = $line.split('","') #used .split('`",`"') as well
$col.length #output value is 18
#cl = $line.split(',')
$cl.length #output value is 6
Why am I seeing two different values here. In both cases ' and "," present same number of times
It seems like I am making some basic mistake. I couldn't figure it out. Any help would be grateful.
Thanks
The only resolvable overload for String.Split() treats the "," string argument as a character array and splits on each occurrence of any of the characters in the array.
Use the -split regex operator instead:
$col = $line -split '","'
If you really hate regex, you can force an overload of String.Split() that takes strings as separators by explicitly casting the first input argument to a string array:
$col = $line.Split([string[]]#('","'),[System.StringSplitOptions]::None)
Why you dont use import-csv command like it?
import-csv "yourpathtofile"
if you want really use get-content you cando it too:
$string='"Some KIND of Test","[STATUS]","TESTNAME","VMNAME","TESTTME","SOME PARAMETER"'
$string | ConvertFrom-Csv -Delimiter "," -Header p1, status, testname, p4, p5 | select status, testname

Powershell - how can I make

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"}

Count number of spaces, and split at the last one

I have a string simliar to:
c:/folder name/somewhere/application.exe instanceName
(n.b. the space in "folder name" is intentional) I need a way to split this into:
[0]c:/folder name/somewhere/application.exe
[1]instanceName
I was going to use split-path, but apparently there is a bug in powershell v2 that stops me doing this:
Split-Path : Cannot find drive. A drive with the name '
So, I figured if I count how many spaces there are, and then simply use -split() to split it at the last space.
But, I can't see how to count the number of spaces.
I've found lots of examples that talk about using regex to count complex strings, but I just want to count spaces!
Tonnes of ways to do this I imagine but to use your split idea you could do the following.
$split = "c:/folder name/somewhere/application.exe instanceName".Split(" ")
$path = $split[0..($split.count -2)] -Join " "
$instance = $split[-1]
Split the sting by spaces. The number of spaces is represented by the count of strings in the array $split. We join all the strings in the array accept the last intp $path then we take the last entry and assign it to $instance
You could also use .substring and .lastindexof
$string = "c:/folder name/somewhere/application.exe instanceName"
$index = $string.LastIndexOf(" ")
$string.Substring(0,$index)
$string.Substring($index + 1)
I can't see a way to split this directly into an array at this time but outputing as an array would not be a big deal.
$path, $instance
or
$array = #($path,$instance)
You can use a regular expression for this:
$s = "c:/folder name/somewhere/application.exe instanceName"
$s -match '(.*) (.*)$'
$matches[1]
$matches[2]
The special variable $matches is populated if the -match operation is true.
$matches[0] contains the original string, and other indexes will exist for the number of groups (patterns in parenthesis) in the regex. In this case: (.*) (.*)$ we have two groups.