Short posts about Microsoft PowerShell as I learn, trying to focus on one aspect at a time.
Showing posts with label Recursion. Show all posts
Showing posts with label Recursion. Show all posts
Tuesday, June 30, 2009
Recursively Getting All Folder Sizes
This started out as my entry for Event 8 in the Microsoft Scripting Games 2009 (see more entries here), but it's useful, so I think I'll keep it. This function uses recursion to work its way down the directory tree and get all folders and their sizes, and outputs them as psobjects for easy sorting, etc. It's not the fastest thing in the world, but it does the trick. Make sure to check out the examples in the help documentation.
Labels:
Get-ChildItem,
Get-DirSize,
PowerShell v2,
Recursion
Monday, May 18, 2009
A Get-ChildItem Wrapper That Lets You Say How Deep to Recurse
I noticed that a couple of people hit my blog that seemed to be looking for a script like this, and it turned out to be an interesting exercise.
Notice that I took the fileglob and moved it down the script rather than just using it in my call to Get-ChildItem. I had to do that because otherwise I would only have been recursing through directories that matched the pattern, and I wanted to recurse down into all directories, but only send those entries that match the pattern down the pipeline.
The function is here, for those that don't see it inline in the post: http://poshcode.org/1113
Labels:
Get-ChildItem,
Get-ChildItemRecurse,
Recursion,
Scripts
Sunday, May 3, 2009
Use PowerShell to Recursively Get Group Members
I've noticed looking at my StatCounter logs that a few people found my post on recursion while looking for a way to recursively get group members in PowerShell, so I whipped up this script.
Note that it depends on the AD Cmdlets from Quest, and that it works in much the same way as the example. Whenever it finds that one of the group members is a group, it calls itself on that group and adds the result to the list of users.
Also note the technique of using a hashtable to keep a list of unique users. It's common to store a list in an array and use use -contains and -notcontains to find out if an item already exists, but this is tremendously inefficient compared to using a hashtable as an index, especially as the list of users gets longer, because it has to keep cycling through the list over and over. It's a trick I picked up in Perl that works equally well here.
Labels:
Get-RecurseMember,
Hashtables,
Quest AD Cmdlets,
Recursion,
Scripts
Tuesday, August 12, 2008
Recursion in PowerShell
Recursion is one of those things that you'll probably find that you don't need to use all that often, but it's a really powerful concept, and where it's useful it can save you a lot of time, and not just in PowerShell. Simply put, a recursive function has the ability to call itself, usually to perform a task with an unknown depth.
I remember the first time I hit this problem, and I had just started scripting. I wanted to see who had permissions to a folder. I walked the ACL on the folder and found a group, so wrote a function to grab the members of the group. That's when I realized that some of the group's members were groups. My first solution was horribly unwieldy. I wrote a bunch of if/else statements that were able to grab group memberships up to three levels deep, and as I was looking unhappily at my hard-to-follow code, a spark of a memory started nagging at me, and I remembered an email thread about recursion, and that's the first time I really "got it".
Let's cut to the chase with an easy example: walking a directory tree. The function below will use Get-ChildItem (dir) to grab all child items in the current PSPath and print their FullName. In order to get all files in all child directories, every time it hits a directory it will call itself on the directory.
# Recurse($path, $fileglob)
#
# Recurses through a psdrive and prints all items that match.
#
# Args:
# [string]$path: The starting path
# [string]$fileglob(optional): The search string for matching files
#
function Recurse ([string]$path, [string]$fileglob){
if (-not (Test-Path $path)) {
Write-Error "$path is an invalid path."
return $false
}
$files = @(dir -Path $path -Include $fileglob)
foreach ($file in $files) {
if ($file.GetType().FullName -eq 'System.IO.FileInfo') {
Write-Output $file.FullName
}elseif ($file.GetType().FullName -eq 'System.IO.DirectoryInfo') {
Recurse $file.FullName
}
}
}
In my mind I always picture a cat's cradle-style string looping through itself over and over until the final loop is met and a final tug unravels the whole thing back into a string.
Sunday, July 20, 2008
The Trouble with Piping
Update: I was wrong, and I'm really happy about it.
Powershell piping works slightly different than I thought. As each result is returned it is passed down the pipe, rather than waiting for the entire section to complete. My earlier tests didn't show what I thought they did, and that was my mistake.
The function I came up with for shortcircuiting on a recursive file search when a matching file is found at the bottom of this poast, but here is another way to do the same thing based on the rest of the the thread that is much simpler.
Note: there is one last caveat to watch out for: if you put parentheses around some code, that code will be executed in its entirety in order to evaluate it before continuing, so keep that in mind when piping commands if you are planning on short-circuiting the process early, as in these examples.
# Find-File($path, $fileglob)
# Returns the first file that matches the fileglob.
#
# Args:
# $path: [string] The path to the directory to start
# searching under
# $fileglob: [string] The filename pattern to match
# (same format as dir)
#
# Returns:
# The file object of the first matching file.
# New Version
function First-File([string]$path, [string]$fileglob){
dir $path -include $fileglob -r | foreach {Write-Output $_; continue}
}
# Old Version
function Find-File ([string]$path, [string]$filename) {
$files = @(dir $path)
foreach ($file in $files) {
if (-not ($file.Mode -match '^d') -and ($file.Name -like "$filename")) {
return $file
}
}
foreach ($file in $files) {
if ($file.Mode -match '^d') {
$result = (Get-File $file.FullName $filename)
if ($result) {
return $result
}
}
}
}
Subscribe to:
Posts (Atom)