Question:
Arrrg!I am running into what i feel is a dumb issue with a simple script i’m writing in powershell. I am invoking a sql command that is calling a stored proc, with the results i put it a array. The results look something like this:
1 2 3 4 |
Status ProcessStartTime ProcessEndTime ------ ---------------- -------------- Expired May 22 2010 8:31PM May 22 2010 8:32PM |
What i’m trying to do is if($s.Contains("Expired"))
, report failed. Simple…? đ Problem i’m running into is it looks like Contains method is not being loaded as i get an error like this:
Method invocation failed because [System.Object[]] doesn’t contain a method named ‘Contains’. At line:1 char:12
+ $s.Contains <<<< (“Expired”)
+ CategoryInfo : InvalidOperation: (Contains:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
So, what can i do to stop powershell from unrolling it to string? Actual ps script below –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$s = @(Invoke-Sqlcmd -Query "USE DB GO exec Monitor_TEST_ps 'EXPORT_RUN',NULL,20" ` -ServerInstance testdb002\testdb_002 ) if ($s.Contains("Expired")) { Write-Host "Expired found, FAIL." } else { Write-Host "Not found, OK." } |
Answer:
The reason you see the method is Get-Members is powershell is trying to be helpful and unrolling the collection. If you have an array with multiple types of items, it shows you the members for each type (like if you âlsâ (Get-ChildItem) and there are FileInfos and DirectoryInfos in the directory you are in, and you pipe ls | gm, it will show you members of FileInfos and also another group of members of DirectoryInfos):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
(7) C:\ -» ls Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 5/28/2010 8:19 AM .hg d---- 5/13/2010 3:37 PM Build ⊠-a--- 4/22/2010 11:21 AM 2603 TODO.org (8) C:\ -» ls | gm TypeName: System.IO.DirectoryInfo Name MemberType Definition ---- ---------- ---------- Mode CodeProperty System.String Mode{get=Mode;} Create Method System.Void Create(System.Security.AccessControl.Direct... ⊠TypeName: System.IO.FileInfo Name MemberType Definition ---- ---------- ---------- Mode CodeProperty System.String Mode{get=Mode;} AppendText Method System.IO.StreamWriter AppendText() ⊠|
What I usually do, to make sure I am not looking at unrolled members, is try â$s.GetType().Nameâ first to see what I am dealing with. In your case, itâs clearly and array, since you initialized it like â$s = @(Invoâ (the @ = itâs an array.)
To find out if an array contains an item, you can use the -contains operator:
1 2 3 |
(9) C:\ -» @(1,2,3) -contains 1 True |
I think youâve got an array of strings, so you can use a string literal, like:
1 2 3 |
(10) C:\ -» @("Stuff","you've","got","might have","Expired") -contains "Expired" True |
But if the expired isnât an exact match (you were looking for an element that contains expired, like âConnection Expired 1/1/2010â) you need to find matches and check the count, I think:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
(23) C:\ -» @("Stuff","you've","got","might have","Connection Expired 1/1/2010") -contains "Expired" False (24) C:\ -» @("Stuff","you've","got","might have","Connection Expired 1/1/2010") | ?{$_.Contains("Expired")} Connection Expired 1/1/2010 (33) C:\ -» $xs = @("Stuff","you've","got","might have","Connection Expired 1/1/2010") | ?{$_.Contains("Expired")} (34) C:\ -» $xs.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True String System.Object (35) C:\ -» $xs Connection Expired 1/1/2010 |
In this case, there is only one match, so powershell unrolled it to a string. Jerk. If there are more than 1 matches, however:
1 2 3 4 5 6 7 8 9 10 11 12 |
(36) C:\ -» $xs = @("Stuff","you've","got","might Expired have","Connection Expired 1/1/2010") | ?{$_.Contains("Expired")} (37) C:\ -» $xs.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array (38) C:\ -» $xs might Expired have Connection Expired 1/1/2010 |
Now itâs an array.
Luckily, both string and array have a length property:
1 2 3 4 5 |
(39) C:\ -» $xs.Length 2 (40) C:\ -» "Bob".Length 3 |
So you can check the length of your results that contain âExpiredâ to see if there are any expiredâs:
1 2 3 4 |
(41) C:\ -» $xs = @("Stuff","you've","got","might Expired have","Connection Expired 1/1/2010") | ?{$_.Contains("Expired")} (42) C:\ -» if ($xs.Length -gt 0) { Write-Host "Whoas, stuff's expired, dog." } Whoas, shit's expired, dog. |
(maybe someone has a better way to check if a collection contains an item that satisifies some predicate (like LINQ’s Any)?)