Cannot find an overload for "ToString" and the argument count: "1" - powershell

i'm not understanding what i'm doing wrong here since i seem to do the same thing but only one works.
i have a text file with a number list that i want to process (round the values):
39.145049
40.258140
41.400803
42.540093
43.664530
and here my script:
$a = get-content "input.txt"
$b = $a -join ','
$b | % {$_.ToString("#.###")}
this results in the following error:
Cannot find an overload for "ToString" and the argument count: "1".
At D:\script.ps1:9 char:9
+ $b | % {$_.ToString("#.###")}
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
however if i take the result after joining which is:
39.145049,40.258140,41.400803,42.540093,43.664530
and create the following script:
$b = 39.145049,40.258140,41.400803,42.540093,43.664530
$b | % {$_.ToString("#.###")}
it works just fine and outputs:
39.145
40.258
41.401
42.54
43.665
where am i going wrong on this one?

This happens as the inputs are not of the same type.
$b1 = $a -join ','
$b2 = 39.145049,40.258140,....
$b1.GetType().Name
String
$b2.GetType().Name
Object[]
As the input in the first case is a single string, foreach loop doesn't process it as a collection of decimal values but a single string. Thus,
$b | % {$_.ToString("#.###")}
Is going to do (as pseudocode):
'39.145049,40.258140,41.400803,42.540093,43.664530'.ToString("#.###")
Whilst the array version is doing
39.145049.ToString("#.###")
40.258140.ToString("#.###")
41.400803.ToString("#.###")
Powershell's able to figure out in the later case that the values are numbers. In the first case, it's just a string and thus the automatic conversion doesn't work.
What actually works in the first case is to cast the values as nubmers. Like so,
$a | % {$([double]$_).ToString("#.###")}
39,145
40,258
41,401
42,54
43,665

Related

Convert a string variable into an integer

I'm trying to convert my $file_data variable into an integer, the bit of code below grabs the number of a computer on my system held in a directory. However when run in PowerShell I get he below error even though the variable is a number.
The '++' operator works only on numbers. The operand is a 'System.String'.
At D:\pscript\Intune.ps1:7 char:26
+ For ($file_data -le 130; $file_data++ ){
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : OperatorRequiresNumber
I'm not sure where I'm going wrong on this any help would be amazing. :)
Get-Content D:\pscript\temp\Directory.txt
$file_data = Get-Content D:\pscript\temp\Directory.txt
For converting string to integer, you can typecast it or declare it at the first point.
[int] $file_data = Get-Content D:\pscript\temp\Directory.txt
However, if this needs to work, Directory.txt should have number which can fit into the category of an integer.

How to extract a certain part of a string in powershell

I want to extract the last 4 digits before ".txt" from this string:
09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK
Those represent the time at which that log was created and I want to display it as 10:58. I read from a file that has multiple lines similar to the one displayed.
Get-Content file.txt | ForEach-Object {
$splitUp = $_ -split "_"
$SC=$splitUp[2] -split "_"
Write-Host $SC
$len = $SC.Length
$folder2 = $SC.Substring($len - 12, 42)
}
I tried separating the string by "_" and then counting the characters in the obtained string and tried separating by the "Substring" command, but I receive the following error.
Exception calling "Substring" with "2" argument(s): "StartIndex cannot
be less than zero. Parameter name: startIndex"
At line:6 char:5
+ $folder2 = $SC.Substring($len - 12, 42)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : ArgumentOutOfRangeException
You can use a regex "lookahead".
What you are searching for is a set of four digits followed by ".txt":
$string = "09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK"
$regex = "\d{4}(?=\.txt)"
[regex]::matches($string, $regex).value
There's probably a more elegant solution:
$String = '09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK'
$String -Match '.*(?=\.txt)' | Out-Null
$Match = $Matches[0][-4..-1] -Join ''
$Time = [DateTime]::ParseExact($Match, 'HHmm',[CultureInfo]::InvariantCulture)
$Time.ToShortTimeString()
Uses RegEx to get all of the string before the .txt
Uses the Array Index to get the characters from 4th to last to the last character and joins them together as a single string.
Casts the value as a DateTime object using ParseExact to interpret it as 24 hour time code
Outputs the Short Date value of that DateTime object.
Just do it with Substring and IndexOf:
$string="09/14/2017 12:00:27 - mtbill_post_201709141058.txt 7577_Delivered: OK"
$string.Substring($string.IndexOf('.txt')-4, 4)

Powershell : Why I cannot use ForEach-Object to modify elements?

I've got an array definition:
> $a={"abc","xyz","hello"}
Then, using foreach to modify it, but seems original elements are not changed:
> foreach($i in $a){$i="kkk"+$i}
> $a
> "abc","xyz","hello"
Why foreach loop doesn't modify elements?
Then I tried to use ForEach-Object, this time, it doesn't even run:
> $a|%{$_=$_+"kkk"}
Method invocation failed because [System.Management.Automation.ScriptBlock] does not contain a method named 'op_Addition'.
At line:1 char:6
+ $a|%{$_=$_+"kkk"}
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Does this has any syntax error? Or my understanding is incorrect?
You don't have an array in $a, you have a [ScriptBlock]. Curly braces {} denote a script block in PowerShell.
The array operator is #().
This is the cause of the error in the second example.
$a=#("abc","xyz","hello")
foreach($i in $a) {
$i = "zzz" + $i
}
$a | % { $_ += "zzz" }
However, this still will not work, because $i and $_ are copies, not references back to the original array location.
Instead, you can iterate over the array using a regular for loop:
for( $i = 0 ; $i -lt $a.Count ; $i++ ) {
$a[$i] += "zzz"
}
In this example you can see that in the loop body, you are referring directly to the array in $a and modifying its actual value.
Also note that ForEach-Object (%) returns a value (or all of the values returned from the block), so you could also do this:
$a = $a | % { "qqq" + $_ }
However this is forming a brand new array and assigning it to $a, not really modifying the original.

Weird PowerShell Exec Output Capture Behavior

I'm writing a simple PowerShell script that handles the output of mkvinfo. It captures the output of mkvinfo, stores in a variable $s and does some post-processing on $s. The strange part is while $s has content, I can't extract a substring from it.
The error message I'm getting was:
Exception calling "Substring" with "1" argument(s): "startIndex cannot be larger than length of string.
Parameter name: startIndex"
This is a sample code:
$filePath = $folder + $file.name
$mkvinfoExe = "C:\mkvinfo.exe"
$s = & $mkvinfoExe $filePath
$s | out-host
$s.Substring($s.Length-1) | out-host
Are you sure $s is a string and not an array? If it is an array, $s.Length will be the number of elements in the array and you could get the error that you are getting.
For example:
PS > $str = #("this", "is", "a")
PS > $str.SubString($str.Length - 1)
Exception calling "Substring" with "1" argument(s): "startIndex cannot be larger than length of string.
Parameter name: startIndex"
At line:1 char:1
+ $str.SubString($str.Length - 1)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentOutOfRangeException
Just found out because mkvinfo outputs multiple lines, $s is actually a String array (or List?). Switching to $s[0].Substring($s[0].Length-1) solves it.

Casting in PowerShell. Weird syntax

Reading XML from a file into a variable can be done like this:
[xml]$x = Get-Content myxml.xml
But why not:
$x = [xml]Get-Content myxml.xml
Which gives:
Unexpected token 'Get-Content' in expression or statement.
At line:1 char:20
+ $x=[xml]get-content <<<< myxml.xml
+ CategoryInfo : ParserError: (get-content:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
That is, why is the cast operation done on the left-hand side of the equals sign? Typically in programming languages the casting is done on the right-hand side like (say) in Java:
a = (String)myobject;
$x = [xml](Get-Content myxml.xml)
In PowerShell everything is an object. That includes the cmdlets. Casting is the process of converting one object type into another.
What this means for casting is that, like in math, you can add in unnecessary brackets to help you clarify for yourself what exactly is happening. In math, 1 + 2 + 3 can become ((1 + 2) + 3) without changing its meaning.
For PowerShell the casting is performed on the next object (to the right), so $x = [xml] get-content myxml.xml becomes $x = ([xml] (get-content)) myxml.xml. This shows that you are trying to cast the cmdlet into an xml object, which is not allowed.
Clearly this is not what you are trying to do, so you must first execute the cmdlet and then cast, AKA $x = [xml] (get-content myxml.xml). The other way to do it, [xml] $x = get-content myxml.xml, is declaring the variable to be of the type xml so that whatever gets assigned to it (AKA the entire right-hand side of the equal sign) will be cast to xml.