Home > Enterprise >  PowerShell, correct way to detect a root parent folder
PowerShell, correct way to detect a root parent folder

Time:10-08

For various applications, I need to get the parent folder of some files to use to create other output.

$x = "C:\MyFolder\test.txt"
$parent = Split-Path (Split-Path $x) -Leaf

This works fine, unless I am near the top level.

$x = "C:\test.txt"
$parent = Split-Path (Split-Path $x) -Leaf

This works, and $parent = C:\ but there is no -Leaf as such. What I am left with could be `D:, E:, F:" or maybe even a network location. What in general would be a good way to deal with these cases?

I could do:

if ($parent.length -eq 3) {
    handle-top-level-situation
}

But this feels a little inelegant. Is there a better way to check on a top level when I am checking for a parent folder name in this way?

CodePudding user response:

A solution based on .NET APIs:

"C:\MyFolder\test.txt", "C:\test.txt" | ForEach-Object { 

  $parent = ([IO.FileInfo] $_).Directory.Name
  if ([IO.Path]::IsPathRooted($parent)) {
    "parent dir. is root path: $parent"
  } else {
    "parent dir. name: $parent"
  }

}

Output:

parent dir. name: MyFolder
parent dir. is root path: C:\

Alternatively, if you want to rule out files in root directories up front, before even trying to extract the parent directory name, you can use a regex with -notmatch:

"C:\MyFolder\test.txt", "C:\test.txt" | ForEach-Object {

  if ($_ -notmatch '^[a-z]:\\. ?\\.|^\\\\. ?\\. ?\\.') {
    "file is located in a root dir: $_"
  } else {
    "parent dir. name: "   ([IO.FileInfo] $_).Directory.Name
  }

}

Note: The above assumes:

  • regular full paths (no accidental doubling of \ such as in c:\\temp), though the regex could be tweaked to handle them.

  • Windows path separators, i.e. \; to make the regex cross-platform, replace all \\ instances with [\\/].

CodePudding user response:

This function may be able to help you get what you're looking for, basically, takes a path as input and it determines if it's a File or Folder Path, what's the Parent Folder and if the Path is Top-Level. I don't have network share to test UNC, could only test it against localhost \ 127.0.0.1...

function Test-Something {
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string] $Path
    )

    process {
        $out  = [ordered]@{ InputPath = $path }

        try {
            $checkFolder = [IO.File]::GetAttributes($Path) -band [IO.FileAttributes]::Directory
        }
        catch {
            $checkFolder = $true
            if([IO.Path]::GetExtension($Path)) { $checkFolder = $false }
        }

        if($checkFolder) {
            $path = Join-Path $Path ([IO.Path]::DirectorySeparatorChar)
            $io   = [IO.DirectoryInfo] $Path
            $out['Parent']   = $io.Parent.Name
            $out['Type']     = 'Directory'
            $out['TopLevel'] = ($true, $false)[[bool] $io.Parent]
        }
        else {
            $io = [IO.FileInfo] $Path
            $out['Parent']   = $io.Directory.Name
            $out['Type']     = 'Archive'
            $out['TopLevel'] = ($true, $false)[[bool] $io.Directory]
        }
        [pscustomobject] $out
    }
}

A few examples:

@(
    'C:'
    'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'
    'C:\doesnotexist'
    'C:\doesnotexist\somefile.ext'
    '\\localhost\c$\Windows'
    '\\127.0.0.1\c$'
) | Test-Something

Output:

InputPath                                                 Parent         Type      TopLevel
---------                                                 ------         ----      --------
C:                                                                       Directory     True
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe v1.0           Archive      False
C:\doesnotexist                                           C:\            Directory    False
C:\doesnotexist\somefile.ext                              doesnotexist   Archive      False
\\localhost\c$\Windows                                    \\localhost\c$ Directory    False
\\127.0.0.1\c$                                                           Directory     True

CodePudding user response:

One way might be to split the string
$x -split '\\' | Select-Object -First 1

The two slashes are just one slash but the second is required for escaping the slash for the split. It results in "C:" but for networkshares it won't return the servername.

For networkshares and local drives you would need
$x -split '\\' | where {$_} | Select-Object -First 1
that does return drivename or servername

Reading your question again and the second, very good answer, i am not sure if you expected something else. So here my third try what you might have expected

function Split-PathToIndex($path, $index) {
    $pathlist = @()
    while ($path) {
        $pathlist  = $path 
        $path = Split-Path -Path $path -Parent
    }
    $pathlist[$index]
}

Examples with the expected output after the "#"

$YourPath = "c:\Test\Path\with\multiple\folder\and\files.txt"
Split-PathToIndex $YourPath 0 # c:\Test\Path\with\multiple\folder\and\files.txt
Split-PathToIndex $YourPath 1 # c:\Test\Path\with\multiple\folder\and
Split-PathToIndex $YourPath 2 # c:\Test\Path\with\multiple\folder
Split-PathToIndex $YourPath 3 # c:\Test\Path\with\multiple
Split-PathToIndex $YourPath 4 # c:\Test\Path\with
Split-PathToIndex $YourPath 5 # c:\Test\Path
Split-PathToIndex $YourPath 6 # c:\Test
Split-PathToIndex $YourPath 7 # c:\

Split-PathToIndex $YourPath -1 # c:\
Split-PathToIndex $YourPath -2 # c:\Test
Split-PathToIndex $YourPath -3 # c:\Test\Path
Split-PathToIndex $YourPath -4 # c:\Test\Path\with
Split-PathToIndex $YourPath -5 # c:\Test\Path\with\multiple
Split-PathToIndex $YourPath -6 # c:\Test\Path\with\multiple\folder
Split-PathToIndex $YourPath -7 # c:\Test\Path\with\multiple\folder\and
Split-PathToIndex $YourPath -8 # c:\Test\Path\with\multiple\folder\and\files.txt

Now i think you would like to have Split-PathToIndex $YourPath -2 so you always get the first folder but i am still unsure :D

all three solutions are not validating if it is a valid/reachable path!

  • Related