Weird PowerShell Exec Output Capture Behavior - powershell

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.

Related

Add elements to arry in powershell

I want to simply add some numbers to an array and then sort them via powershell, however, the following code seems to be wrong
$myArray = New-Object System.Collections.ArrayList
Foreach ($Name in $VMName) {
$Tokens = $Name.Split(".")
$myArray.Add($Tokens[$Tokens.Count-1])
}
Write-Host($myArray | Sort-Object)
The error is
+ $myArray.Add($Tokens[$Tokens.Count-1])
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException
How can I fix that?
The variable $Name is something like 101.u18.uab.14 or 103.win10.template or 102.win7.pink.18 and so on. Each $Name has some . symbols and I want to tokenize them and get the last element for each of them. So, in this example, I want to see a sorted 14 18 template.
UPDATE:
The provided methods seems to be incorrect.
1- This method by Steven
$myArray = New-Object System.Collections.ArrayList
Foreach ($Name in $VMName) {
$Tokens = $Name.Split(".")
[Void]$myArray.Add($Tokens[-1])
}
shows this error
Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."
At C:\Users\user\Desktop\get_ip_list.ps1:20 char:5
+ [Void]$myArray.Add($Tokens[-1])
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException
2- This method by Santiago
$myArray = [Collections.Generic.List[string]]::new()
Foreach ($Name in $VMName) {
[Void]$myArray.Add($Name.Split(".")[-1])
}
Shows the following error
Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."
At C:\Users\user\Desktop\get_ip_list.ps1:19 char:5
+ [Void]$myArray.Add($Name.Split(".")[-1])
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException
If I have missed your point in the above codes, please let me know.
I think you are missing the first line from the error. However it looks like you are simply trying to add the last elements from the $Tokens array. In that case you don't need to reference the index like that, below should work:
$myArray = New-Object System.Collections.ArrayList
Foreach ($Name in $VMName) {
$Tokens = $Name.Split(".")
[Void]$myArray.Add($Tokens[-1])
}
Notice the addition of [Void] this will stop the .Add() method from returning the index number it just added to.
Also note you can create array list objects using casting like:
$myArray = [Collections.ArrayList]#()
Update to Address Continued Errors:
The only thing I can think of to cause the error "Collection was of a fixed size." is if you've previously type constrained the variable.
Example:
[String[]]$myArray = #()
# Posibly a whole bunch of other things happening maybe in the console or IDE.
$myArray = [Collections.ArrayList]#()
$myArray.Add('something')
Results:
Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."
At line:1 char:1
+ $myArray.Add('something')...
In this case they type of the $myArray will not change to [Collections.ArrayList]. The problem will be transparent up until you try to use the .Add() method that won't work. This is because an array list is easily and therefore silently cast back to a [String[]] or [Object[]].
Note: If you were to run $myArray.IsFixedSize it would return "True".
My guess as to what's happening; at some point while developing your code or perhaps in the larger script, $myArray got type constrained, and stuck in the scope. This can definitely happen especially given the scope overlap in IDE's like PowerShell's ISE, and I think it happens in VSCode as well. If this is part of a larger script look for instances of $myArray to see if it's indeed type constrained and make corrections as needed. Otherwise a simply restarting your session might do the trick.
Honestly, not sure how could you be getting that error unless the array we're looping through is actually something different. Steven's answer should work fine, I'll put this code below just to show that the results we get are the ones you expect:
$col = [Collections.Generic.List[String]]::new()
$vmName = #(
'101.u18.uab.14'
'103.win10.template'
'102.win7.pink.18'
)
ForEach($name in $vmName)
{
$col.Add($name.Split('.')[-1])
}
if you want absolutly use array you can simply do it :
$Array=#()
$VMName | %{
$Value=($_.Split('.'))[-1]
$Array+= $Value
}
$Array| sort
Otherwise you can simply do it :
$VMName | %{($_.Split('.'))[-1]} | sort

How to get this PowerShell command output into an iterable collection or array

This answer shows how to output a list of computer-names on the domain:
(([adsi]"WinNT://$((Get-WMIObject Win32_ComputerSystem).Domain)").Children).Where({$_.schemaclassname -eq 'computer'})
If the Pathproperty is appended
... .Where({$_.schemaclassname -eq 'computer'}).Path
it outputs a list in this format:
WinNT://{domainname}.net/{machine-name}
How would that succession of fully qualified machine names be made into an array or a collection that could be iterated by the script found here?
Can that command (i.e. its output) simply be assigned to an array variable?
[array]$ComputerNames = ... (([adsi]"WinNT://$((Get-WMIObject <snip> ).Path
I suspect not, since when I try that
$ComputerNames = ... (([adsi]"WinNT://$((Get-WMIObject <snip> ).Path
foreach($computer in $ComputerNames) {
if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer)) {
I get an error:
Exception calling "OpenRemoteBaseKey" with "2" argument(s): "The network path was not found."
At C:\Users\{myusername}\Get-NetFrameworkVersion.ps1:40 char:8
+ if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Lo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IOException
My ultimate goal is to obtain the .NET versions installed on all PCs on the domain.

PowerShell - Add-Content- Unable to add multiple vars to a file

I'm trying to add an expression to a log file which contains Date,Time some data separated by ";". Unfortunately I get an error every time I change the position of the items in the -value brackets.
Whats seems to be wrong?
This is the code :
Add-Content -path C:\...\outlog.txt -Value($Date + ';' + $Time + ';Checked;' + $strFileName)
This is the error :
Cannot convert argument "1", with value: ";", for "op_Addition" to type "System.TimeSpan": "Cannot convert
value ";" to type "System.TimeSpan". Error: "String was not recognized as a valid TimeSpan.""
At C:\...\Untitled1.ps1:8 char:64
+ ... \outlog.txt -Value($($Date + ';' + $Time + ';'+ $str))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
Try this -
Add-Content -path C:\...\outlog.txt -Value("$Date; $Time; Checked; $strFileName")
If you look at get-help Add-Content -full, and look at the -value parameter, you will see -
-Value <Object[]>
Specifies the content to be added. Type a quoted string, such as "This data is for internal use only", or
specify an object that contains content, such as the DateTime object that Get-Date generates.
You cannot specify the contents of a file by typing its path, because the path is just a string, but you can
use a Get-Content command to get the content and pass it to the Value parameter.
Required? true
Position? 1
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? false
It says that it expects a quoted string or an object that contains content. It was missing in your case and hence the + operator was trying to add $date and time.

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 error 'can't call null-value expresssion' [duplicate]

I am simply trying to create a powershell script which calculates the md5 sum of an executable (a file).
My .ps1 script:
$answer = Read-Host "File name and extension (ie; file.exe)"
$someFilePath = "C:\Users\xxx\Downloads\$answer"
If (Test-Path $someFilePath){
$stream = [System.IO.File]::Open("$someFilePath",[System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
$hash = [System.BitConverter]::ToString($md5.ComputeHash($stream))
$hash
$stream.Close()
}
Else{
Write-Host "Sorry, file $answer doesn't seem to exist."
}
Upon running my script I receive the following error:
You cannot call a method on a null-valued expression.
At C:\Users\xxx\Downloads\md5sum.ps1:6 char:29
+ $hash = [System.BitConverter]::ToString($md5.Compute ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
To my understanding, this error means the script is attempting to do something, but another part of the script does not have any information to permit the first part of the script to work properly. In this case, $hash.
Get-ExecutionPolicy outputs Unrestricted.
What is causing this error?
What exactly is my null valued expression?
Any help is appreciated. I apologize if this is trivial and will continue my research.
References:
http://blogs.technet.com/b/heyscriptingguy/archive/2013/03/27/troubleshoot-the-invokemethodonnull-error-with-powershell.aspx
How to get an MD5 checksum in PowerShell
The simple answer for this one is that you have an undeclared (null) variable. In this case it is $md5. From the comment you put this needed to be declared elsewhere in your code
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
The error was because you are trying to execute a method that does not exist.
PS C:\Users\Matt> $md5 | gm
TypeName: System.Security.Cryptography.MD5CryptoServiceProvider
Name MemberType Definition
---- ---------- ----------
Clear Method void Clear()
ComputeHash Method byte[] ComputeHash(System.IO.Stream inputStream), byte[] ComputeHash(byte[] buffer), byte[] ComputeHash(byte[] buffer, int offset, ...
The .ComputeHash() of $md5.ComputeHash() was the null valued expression. Typing in gibberish would create the same effect.
PS C:\Users\Matt> $bagel.MakeMeABagel()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $bagel.MakeMeABagel()
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
PowerShell by default allows this to happen as defined its StrictMode
When Set-StrictMode is off, uninitialized variables (Version 1) are assumed to have a value of 0 (zero) or $Null, depending on type. References to non-existent properties return $Null, and the results of function syntax that is not valid vary with the error. Unnamed variables are not permitted.