views:

279

answers:

4

I would like to find all directories at the top level from the location of the script that are stored in subversion.

In C# it would be something like this

Directory.GetDirectories(".")
  .Where(d=>Directories.GetDirectories(d)
     .Any(x => x == "_svn" || ".svn"));

I'm having a bit of difficulty finding the equivalent of "Any()" in powershell and I don't want to go through the awkwardness of calling the extension method.

So far I've got this:

 gci | ? {$_.PsIsContainer} | gci -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"

This finds me the svn directories themselves but not their parent directories - which is what I want. Bonus points if you can tell me why adding

 | select-object {$_.Directory}

to the end of that command list simply displays a sequence of blank lines.

+3  A: 

Unfortunately there is no equivalent in PowerShell. I wrote a blog post about this with a suggestion for a general purpose Test-Any function / filter.

function Test-Any() {
    begin {
        $any = $false
    }
    process {
        $any = $true
    }
    end {
        $any
    }
}

Blog Post: http://blogs.msdn.com/jaredpar/archive/2008/06/12/is-there-anything-in-that-pipeline.aspx

JaredPar
This is quite a simple but yet effective answer.
Sung Meister
A: 

Ended up doing it with a count:

$directoryContainsSvn = {
    (gci $_.Name -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"} | Measure-Object).Count -eq 1
}
$svnDirs = gci | ? {$_.PsIsContainer} | ? $directoryContainsSvn
George Mauer
Note that you can use the complete pipeline as a boolean test anyway, since any non-empty collection evaluates to `$true` (except the one that contains `$false` as the only element—but that's of no concern here). Also your test is messed-up because the precedence of `-and` and `-or` mean that you will catch *files* that happen to be named `".svn"` too, since the `PSIsContainer` test only applies when the name matches `"_svn"`.
Joey
Thanks Johannes, thats good to know
George Mauer
+4  A: 

My approach now was:

gci -r -force `
    | ? { $_.PSIsContainer -and $_.Name -match "^[._]svn$" } `
    | select Parent -Unique


The reason why

select-object {$_.Directory}

doesn't return anything useful is that there is no such property on a DirectoryInfo object. At least not in my PowerShell.


To elaborate on your own answer: PowerShell can treat most non-empty collections as $true, so you can simply do:

$svnDirs = gci `
    | ? {$_.PsIsContainer} `
    | ? {
        gci $_.Name -Force `
            | ? {$_.PSIsContainer -and ($_.Name -eq "_svn" -or $_.Name -eq ".svn") }
        }
Joey
ooh, its directory info? That clears somethins up
George Mauer
`Get-ChildItem` returns both `FileInfo` and `DirectoryInfo` objects :-)
Joey
IMO, this is a better answer than @JaredPar's, because you can test for pipeline being empty as described here. I think the answer could be improved by moving the | to the line before, and removing the `.
Jay Bazuzi
I tend to break up my piepelines like this for readability. Might just be a matter of personal taste. But imho it definitely reads better than a 200-character line.
Joey
What Jay means is that a | character at the end of the line automatically continues the code to the next line (just like an open bracket without a closed bracket) so that you don't need to escape the return character.
JasonMArcher
Yes, but then I need to look at the *previous* line to know *why* there has been a line break (pipeline, block start, etc.) instead of immediately knowing how the pipeline flows when I see the start of the lines.
Joey
+2  A: 

You can tighten this up a bit:

gci -fo | ?{$_.PSIsContainer -and `
            (gci $_ -r -fo | ?{$_.PSIsContainer -and $_ -match '[_.]svn$'})}

Note - passing $__.Name to the nested gci is unnecessary. Passing it $_ is sufficent.

Keith Hill