Hello there, i am facing a problem that i could not resolve, and i am stucked
I made a function that return all the directories and subdirectories of a given path and depth.
I know that in Powershell V5 the -depth parameter is included in the gci cmdlet, but im dealing with old windows servers (with PS v2), so i have to
make a compatible function with old versions
The problem is, that i want to list ALL subdirectories (inlcuding those were i don´t haver permission).
But, the issue is that i don´t retrieve those folders were i dont have permission. Those folders remains like hidden.
The only solution i found, i to use -recurse, which doesn´t solve my problem, cause -recurse go through all the directories untill end.
So basically, i need a way to list ALL directories (also those directories i dont have permission) until the -depth value that i put.
I transcribe the part that does this task
....
if ($depth){
for($i=1;$i -le $depth;$i++){
$actualSubdirectory=$symbol * $i
$subfolders+= Get-ChildItem $drive$actualSubdirectory -ErrorVariable +err
-ErrorAction SilentlyContinue | ? {$_.psiscontainer -eq $true} |
Select-Object -ExpandProperty FullName
}
}
elseif (-not $depth){
#
$subfolders = Get-ChildItem $drive -Recurse -ErrorVariable +err `
-ErrorAction SilentlyContinue | ? {$_.psiscontainer -eq $true} |
Select-Object -ExpandProperty FullName
}
....
example:
$symbol=’*’
$drive=‘C:\temp\carpeta1’
I will apreciatte any help in advance.
Thanks a lot!
If you want to specify depth, you’d have to implement recursion manually. I would have to imagine that someone has done this before, but this is a basic example:
function Get-ChildItemCustom {
[CmdLetBinding()]
param (
$Path,
$Depth
)
begin {
}
process {
foreach ($item in Get-ChildItem -Path $Path ) {
if ($item.PSIsContainer) {
Write-Verbose (($item.fullname -split '\\').Count)
if (($item.fullname -split '\\').Count -le $Depth) {
Get-ChildItemCustom -Path $item.FullName
}
else {
Write-Verbose "Stopping because of depth param"
}
}
else {
$item
}
}
}
end {
}
}
Get-ChildItemCustom -Path C:\Windows -Depth 3 -Verbose
Output:
C:\Windows\Provisioning\categories.xml
C:\Windows\Provisioning\IccidToRegion.xml
C:\Windows\Provisioning\Microsoft-Common-Provisioning.dat
C:\Windows\Provisioning\Microsoft-Desktop-Provisioning.dat
VERBOSE: 3
VERBOSE: 4
VERBOSE: Stopping because of depth param
C:\Windows\Registration\R000000000001.clb
C:\Windows\Registration\R00000000000c.clb
C:\Windows\Registration\R00000000000d.clb
C:\Windows\Registration\{02D4B3F1-FD88-11D1-960D-00805FC79235}.{D...
VERBOSE: 3
VERBOSE: 4
VERBOSE: Stopping because of depth param
VERBOSE: 4
VERBOSE: Stopping because of depth param
VERBOSE: 4
VERBOSE: Stopping because of depth param
C:\Windows\rescache\ResCache.mni
isnt this what your looking for?
Get-ChildItem d:\ -Directory -Depth 2
Rob Simmers, it´s very near what i am looking for. A recursive way
I was trying tu modify it, so i can receive a PS custom object.
foreach ($item in (Get-ChildItem $drive -ErrorAction SilentlyContinue | Where-Object {$_.psiscontainer -eq $true})){
if (($item.FullName -split "\\").Count -le $depth){
$hashTable=@{
Folders=Get-ChildItem -Path $item.FullName -ErrorAction SilentlyContinue -ErrorVariable +err
Errors=$err
}
$prop=New-Object -TypeName psobject -Property $hashTable
$script:folderArray+=$prop
get-AclLocallyLocal -drive $item.FullName
}
else{
break;
}
}
return $script:folderArray
But i get the result more than one time. I get a line for each recursive pass.
And also i dont retrieve a folder that i haver permission
Here is the structure and the result i get
C:\Windows\Temp\Folder
Folder 1 (No Permission)
Folder 2 (with permission)
Get-AclLocallyLocal C:\Windows\Temp\Folder\ -depth 5
Result:
Errors Folders
------ -------
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
Chris Bakker, yeap. But i´m dealing with windows servers 2003, which have powershell v2, and therefore do not have the -depth parameter 
There are several bad practices in your code.
-
$script:folderArray+=$prop is writing to a global variable versus having a function return results.
function Get-ChildItemCustom {
[CmdLetBinding()]
param (
$Path,
$Depth
)
begin {
}
process {
#Everything that occurs in the loop is written to $results, no need to +=
$results = foreach ($item in Get-ChildItem -Path $Path ) {
if ($item.PSIsContainer) {
Write-Verbose (($item.fullname -split '\\').Count)
if (($item.fullname -split '\\').Count -le $Depth) {
Get-ChildItemCustom -Path $item.FullName
}
else {
Write-Verbose "Stopping because of depth param"
}
}
else {
$item.fullname
}
}
}
end {
$results
}
}
$myResults = Get-ChildItemCustom -Path C:\Windows -Depth 3
$myResults
-
break; will break the current loop which you need the loop to continue for each item, break stops the enumeration
-
$err is unnecessary. The $Error global variable will contain any failures that occured. You may want to add $Error.Clear() to the begin{} block to clear existing errors and it any Get-ChildItem failure will be logged there. I've had a seperate function that parsed the $Error varaible to get Get-ChildItem failures
PS C:\Users\Rob> $Error.Clear()
PS C:\Users\Rob> #run code
PS C:\Users\Rob> $Error
Get-ChildItem : Access to the path ‘C:\Windows\Prefetch’ is denied.
At line:12 char:38
Get-ChildItem : Access to the path ‘C:\Windows\ModemLogs’ is denied.
At line:12 char:38
Get-ChildItem : Access to the path ‘C:\Windows\Minidump’ is denied.
At line:12 char:38
Get-ChildItem : Access to the path ‘C:\Windows\LiveKernelReports’ is denied.
At line:12 char:38
Get-ChildItem : Access to the path ‘C:\Windows\InfusedApps’ is denied.
At line:12 char:38
Rob, thangs again for your quickly help !!. Really apreciatted.
I was using your workarround, and change the output to a customobject.
function Get-AclLocallyLocal_test2 {
[CmdLetBinding()]
param(
[Parameter(Mandatory=$true)]
[String]$drive,
[Parameter(Mandatory=$false)]
[int]$depth
)
BEGIN{
}#BEGIN
PROCESS {
foreach($item in get-childitem -path $drive -ErrorAction SilentlyContinue){
#if is directory
if ($item.psiscontainer){
#if amount of directories are equal or lower than specified depth
if(($item.fullname -split '\\').count -le $depth){
$aclLocalyHashtable=@{
Directory=$item.fullname
}
$aclLocallyObject = new-object -TypeName psobject -Property $aclLocalyHashtable
$aclLocallyObject
Get-AclLocallyLocal_test2 $item.fullname -depth $depth
}
elseif(!($item.fullname -split '\\').count -le $depth){
continue
}
}#if psiscontainer
else{
continue
}
}#foreach
}#PROCESS
END{
#$aclLocallyObject
}#END
}
But i cant manage the errors within the script.
I dont know if it is posible to return a customobject with 2 keys
Directory (all directories)
Errors ( a nested customobject in this key, with the errors details)
I write it here to graphic what i am trying to say (sorry if i am doing wrong.I´m trying to do my best and learn)
function Get-AclLocallyLocal_test3 {
[CmdLetBinding()]
param(
[Parameter(Mandatory=$true)]
[String]$drive,
[Parameter(Mandatory=$false)]
[int]$depth
)
BEGIN{
}#BEGIN
PROCESS {
foreach($item in get-childitem -path $drive -ErrorAction SilentlyContinue -ErrorVariable +err){
#if is directory
if ($item.psiscontainer){
#if amount of directories are equal or lower than specified depth
if(($item.fullname -split '\\').count -le $depth){
if ($err){
$errors=@{
Target=$err.Get_categoryinfo().TargetName
Category=$err.Get_categoryinfo().Category
Reason=$err.Get_categoryinfo().Reason
}
}
$aclLocalyHashtable=@{
Directory=$item.fullname
Error=$errors
}
$aclLocallyObject = new-object -TypeName psobject -Property $aclLocalyHashtable
$aclLocallyObject
$err.clear()
Get-AclLocallyLocal_test2 $item.fullname -depth $depth
}
elseif(!($item.fullname -split '\\').count -le $depth){
continue
}
}#if psiscontainer
else{
continue
}
}#foreach
}#PROCESS
END{
}#END
}
Sorry to be such a bore, but i´m trying hard to resolve it.
I know im near, but i can´t get a hashtable of the directories and the directories with no permissions.
I only get the hashtable with directories, and in the errors column i receive nothing.
Here is my code
## Get-AclLocallyLocal
function Get-AclLocallyLocal_test2 {
[CmdLetBinding()]
param(
[Parameter(Mandatory=$true)]
[String]$drive,
[Parameter(Mandatory=$false)]
[int]$depth
)
BEGIN{
}#BEGIN
PROCESS {
foreach($item in get-childitem -path $drive -ErrorAction SilentlyContinue -ErrorVariable +err){
#if is directory
if ($item.psiscontainer){
#if amount of directories are equal or lower than specified depth
if(($item.fullname -split '\\').count -le $depth){
$aclLocalyHashtable=@{
Directory=$item.fullname
Error=""
}
$aclLocallyObject = new-object -TypeName psobject -Property $aclLocalyHashtable
$aclLocallyObject
Get-AclLocallyLocal_test2 $item.fullname -depth $depth
}
elseif(!($item.fullname -split '\\').count -le $depth){
continue
}
}#if psiscontainer
else{
continue
}
}#foreach
}#PROCESS
END{
#$aclLocallyObject
}#END
}
Get-AclLocallyLocal_test2 -drive "C:\temp\CarpetaPrueba" -depth 5
And here is the output:
Error Directory
----- ---------
C:\temp\CarpetaPrueba\Carpeta1
C:\temp\CarpetaPrueba\Carpeta2
C:\temp\CarpetaPrueba\Carpeta3
Again, sorry to bother you, but i really need this.
Thanks a lot.