Powershell evaluating against each member of collection rather than collection - powershell

I have the following code:
$a = gci .\Areas -Recurse
($a[0].EnumerateFileSystemInfos()).Count
This is its output
PS C:\> ($a[0].EnumerateFileSystemInfos()).Count
1
1
1
1
1
1
Why? When I run gm -InputObject $a[0], I clearly see that a collection is returned.
EnumerateFileSystemInfos Method System.Collections.Generic.IEnumerable[System.IO.FileSystemInfo] EnumerateF...
Why is it evaluating .Count against each member of the collection rather than the collection itself? Also worth noting is that
($a[0].EnumerateFileSystemInfos()).Count()
returns an error:
Method invocation failed because [System.IO.FileInfo] does not contain a method named 'Count'.
At line:1 char:1
+ ($a[0].EnumerateFileSystemInfos()).Count()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Which is what I would expect if I were calling it against $a[0][0] but I'm not. What's going on and how can I retrieve the number of items in the collection?

EnumerateFileSystemInfos() returns an IEnumerable, more precisely, a System.IO.FileSystemEnumerableIterator'1, thus each query to it returns a single object. And when you're piping the output to Out-Default, that cmdlet checks if the IEnumerable has more data, if yes, queries it again. That's why you get a sequence of 1's, because each object behind an enumerable is a single object and not an array.
You should instead use GetFileSystemInfos() to get your proper count, it returns an array.

To get the number of items in the the collection$a:
$a.Count
I don't understand the need for added complexity, and a .NET/C# approach. Is there anything this provides that you require?

Related

Using PowerShell, how can I use Substring to extract the computer name from output

I am new to PowerShell but I found I can use Substring to count to the right or left of a string within a variable. It appears though it is not supported for the output I am receiving. I am hoping someone can point me in the right direction. Thank you for any help.
Code to retrieve the computer name.
$compname = WmiObject -class Win32_ComputerSystem | Select-Object Name
$compname
$compname.Substring(9,0)
Here is the result and error:
Name
Computer-PC
Method invocation failed because [Selected.System.Management.ManagementObject] does not contain a method named 'Substring'.
At line:3 char:1
$compname.Substring(9,0)
+ CategoryInfo : InvalidOperation: (Substring:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
This error occurs because you're trying to use the Substring method on an object.
Take a look, if i do the same query that you did, it returns me an object with "Name" property:
And as the powershell error shows, you cannot call the substring method directly to an object. You must do it on a string, in this case, the property name. To solve you problem, you just need to call "Name" property in your query. Something like this:
$computerName = (Get-WmiObject Win32_ComputerSystem).Name
After that, you will be able to use "Substring" method because that query returns a string:
If any other problem occurs, i will be glad to help you :)

You cannot call a method on a null-valued expression - general

You create a script, it works for some time, then out of the blue it starts crashing with "You cannot call a method on a null-valued expression" or "The property 'property name' cannot be found on this object. Verify that the property exists and can be set.". What does this mean?
This is a Powershell version of "null pointer exception". This exception arises every time you attempt to query a variable that appears to be null. To determine what variable is null and where, you need to read the stack trace and the line/symbol numbers of the line in question. An example:
You cannot call a method on a null-valued expression.
At E:\temp\testsest.ps1:35 char:12
+ If($Search.value() -contains $SearchString)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Let's parse the error message. First, there is a wording that is in the title of this question. If you are about to ask a question with this wording, you will get a set of similar questions proposed by StackOverflow. But there is more in the error description. Second line shows script, line and character number of the first character of an expression that generates this exception. Here, a request is made to $Search.value() querying if it -contains $SearchString. The wavy underline separates the expression in full, although the proper way would be underlining only $Search.value(). Next, there is a CategoryInfo and FullyQualifiedErrorId, the latter saying "Invoke method on null", omitting "pointer" or "variable".
Now, let's debug the message. Here, the only method that's about to be called is value(), this means $Search is equal to null. Therefore, we need to get upwards from the line 35 of the script and find a place where a value is last assigned to the variable in question. This particular script had a query to Range.Find() which returns null if there's no match to the searched string. An excerpt:
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$ExcelWorkBook = $Excel.Workbooks.Open($ExcelPath)
$ExcelWorkSheet = $Excel.WorkSheets.item("$location")
$Range = $ExcelWorkSheet.Range("A1").EntireColumn
$Search = $Range.find($user) # <<< here we get null
If($Search.value() -contains $user)
So, we have found where do we receive a null.
Remedies vary, but all include checks against $null. In this case it's enough to check $Search for null, and return "Nothing found" if it is indeed null. It might be not as simple, there might be more structures that can be null, like in $a.b.c.someMethod() - here either $a, $a.b or $a.b.c is null, so you need to check all of the outcomes. There are also situations where a complex structure is returned, and is expected to have a value in a certain field, but the field is not populated, therefore trying to use that field's value will produce an exception.
The moral is: If you receive an exception speaking about "null-valued", you have not expected something to return null, and you have to add checks for null (or, in fact, any unexpected) values before attempting to use the result.

Querying Active Directory user information using Powershell - seemingly equivalent syntax, different results?

I have a simple Powershell function to perform an Active Directory LDAP lookup based on the SID of a user:
function SidToAdUser($sid) {[adsi]("LDAP://<SID=" + $sid + ">")}
If I wish to read an attribute from the returned User object, accessing it via an intermediary variable works fine:
$ad = SidToAdUser("S-1-5-21-968173855-142910291-87512543-670313")
$ad.department
However, attempting to access it directly from the return value of the function, like this:
SidToAdUser("S-1-5-21-968173855-142910291-87512543-670313").department
elicits an error:
format-default : The following exception occurred while retrieving member "distinguishedName": "An invalid dn syntax has been specified.
"
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
Can anyone advise why exactly this would be the case, and how to correct it?
Thank you.
Your function call syntax is wrong.
(SidToAdUser S-1-5-21-968173855-142910291-87512543-670313).department
In powershell, function arguments are specified as space-separated values after the function name, not enclosed in parens.

Why would a variable in PowerShell lose its value after it is referenced?

With the following 2 lines of code:
$meta = New-Object System.Management.Automation.CommandMetadata (Get-Command Get-Event)
$parametersInCmdlet = $meta.Parameters.GetEnumerator()
The $parametersInCmdlet variable is set as can be seen by referencing it.
$parametersInCmdlet
Key Value
--- -----
SourceIdentifier System.Management.Automation.ParameterMetadata
EventIdentifier System.Management.Automation.ParameterMetadata
When I reference it again immediately after that, it appears empty (and confirmed if piped to Get-Member).
$parametersInCmdlet | gm
gm : No object has been specified to the get-member cmdlet.
At line:1 char:23
+ $parametersInCmdlet | gm
+ ~~
+ CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
+ FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
There is nothing else [that should be] touching that variable in between those references. This occurs in the console and ISE for both PS 2.0 and 3.0 so that makes me think it is more user misunderstanding than a bug.
What would cause the value to be lost in this case?
The object returned by GetEnumerator() methods is pretty much always an IEnumerator. The job of an IEnumerator is to hand back elements of a collection, one at a time, until that collection is depleted. At that point, it is the correct behavior for the IEnumerator to return back nothing when asked for the next item.
Powershell unrolls the entire collection when you look at it the first time. Thus, by default, it is expected that you can't look at the collection again, since the IEnumerator has already been "spent."
The workaround is to call Reset() on the IEnumerator if you want it to start over. Assuming the IEnumerator is properly implemented, this will allow you to re-read the collection from the beginning again.
So, try calling $parametersInCmdlet.Reset() before using it again.

How to get some content from xml file by powershell?

Variable $returnedxml is a sql query result forming in xml. I need to get content 'releasepath'\\sharespace\test1\\10.0.1212.00from it
<ReleasePath>\\sharespace\test1\\10.0.1212.00</ReleasePath>
Here are my code:
$xmldoc= new-object xml.xmldocument
$xmldoc.load($Returnedxml)
$xmldoc.releasepath
Here are the returned error alarm:
Exception calling "Load" with "1" argument(s): "Could not find file 'C:\Users\admin\System.Xml.XmlDocument'."
At D:\connecttods3andinvoke.ps1:47 char:14
+ $xmldoc.load <<<< ($Returnedxml)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I thought xml.xmldocument is a .net class, seems that I was wrong. So what can I do then?
Since the handling of XML data is so integral to so many management tasks, Powershell has an XML Type Accelerator. So this would work as well:
[xml]$xmldoc = $returnedxml
$xmldoc.releasepath
I just read it into a string ...
$file = [IO.File]::ReadAllText($filename)
then use xpath to get values out of it ...
$releasePath = $file | SelectXml "//ReleasePath"
XPath is really powerful for pulling things out of an XML file, much simpler (coding wise) than using xmldoc
Your variable $Returnedxml must be a file name with absolute path. But currently it is an object of class System.Xml.XmlDocument.
So change your variable and then you can read the file.
Or on the other hand if you already have an object of XmlDocument in $Returnedxml then you do not have to read it into $xmldoc. Both are from the same class. Just use $Returnedxml
You are using the wrong load; that one is for files. Use LoadXML instead:
$xmldoc.LoadXml($Returnedxml)