Fun with Paths

PowerShell Team

PowerShell Team

Jeffrey wrote a great post for wizards about the scope of variables in PowerShell, so to maintain balance, I thought I’d weigh in with one for newbies. (Although, to be honest, Jeffrey’s description is clear enough for the rest of us.)

 

There are some newbies here, right?

 

In a thread about Move-Item on Microsoft.Public.Windows.PowerShell, PowerShell tester-extraordinaire Marcel Ortiz Soto suggested a clever method for deleting the current directory from a path. You might want to study this method and store it away for future use.

 

As a bonus, it shows how do string manipulation on PathInfo and FileInfo objects. Essentially, you use string methods on the string properties of these non-string objects. (Easier done than said…)

 

By the way, Marcel’s solution uses the $pwd automatic variable, which always contains the path to the current directory.

 

      PS C:\Windows> $pwd

      Path

      —-

      C:\Windows

 

$pwd is missing from the version of about_automatic_variables shipped with Windows PowerShell 1.0, but it will appear in updates. Sorry about that.

 

 

The Problem

 

A user wanted to move selected files from one directory to another while preserving their directory structure. Because the files were being piped to Move-Item one at a time from a Get-ChildItem command, the directory structure was lost.

 

The solution was to specify a new directory path for each file that consisted of a new location, but the current subdirectories and file name.

 

The original path:

 

      C:\Windows\<subdirectories\filename>

 

The new path:

 

      D:\Archive\<subdirectories\filename>

 

The task is to remove the current directory (C:\Windows) from the path and append the remainder of the path to D:\Archive.

 

 

The  Solution

 

Here is Marcel’s solution:

 

get-childitem | where {conditions} | move-item -destination {join-path ‘D:\Archive’ $_.FullName.SubString($pwd.path.length) }

 

 

The Method: Eliminate the current directory from a path

 

My first inclination was to use the Split-Path cmdlet to eliminate the current directory part of the path, but it doesn’t let you distinguish between parts of a “Parent” path. Thus, the solution requires some string manipulation.

 

To delete the current directory from a file or directory path:

 

1.       Find the length (the number of characters) of the current directory path. Use the Length property of the path, which is a string.

 

    PS C:\Windows> $pwd                 # a PathInfo object

    Path

    —-

    C:\Windows

 

    PS C:\Windows> $pwd.path            # a string

    C:\Windows

 

    PS C:\Windows> $pwd.path.length     # the length of the path string

    10

 

(HINT: $pwd is a PathInfo object, so it doesn’t have a Length property, but its Path property is a string, which does have a Length property.)

 

 

2.       Find the original, fully-qualified path of the file, which includes the current directory. Use the FullName property of the file (or directory), which is a string.

 

PS C:\Windows> get-item C:\Windows\Dev\tmp.txt         # FileInfo object

 

PS C:\Windows> (get-item C:\Windows\Dev\tmp.txt).fullname C:\Windows\Dev\tmp.txt

 

(HINT: We use FullName property to get a string object that contains the file path. This allows us to do the next step, which requires a string.)

 

3.       Use the SubString method on the FullName property of the original file path. The SubString method eliminates the specified number of characters, and then returns the remainder of the string.

 

For the value of substring, that is, the number of characters to eliminate, use $pwd.path.length which represents the number of characters in the current directory path.

 

<File>.FullName.Substring(<length-of-current-directory-path>)    

 

PS C:\Windows>(get-item C:\Windows\Dev\tmp.txt).FullName.SubString($pwd.path.length)

 

\Dev\tmp.txt

 

In essence, we take the original path in string form (its FullName):

C:\Windows\Dev\tmp.txt

 

Then we eliminate the number of characters in the path to the current directory ($pwd.path.length = 10 characters).

 

C:\Windows\Dev\tmp.txt

 

And return the remainder of the string (the “substring”):

 

\Dev\tmp.txt

 

(HINT: You can’t use the SubString method on the file path, which is a FileInfo object, but you can use it on the value of the FullName property of the FileInfo object, which is a string.)

 

For more information about the Substring method, see:        

   http://msdn2.microsoft.com/en-us/library/aa904307(VS.71).aspx.)

 

 

Create a New Path   

 

The final step is to append the remainder of the path to the new location, D:\Archive.

 

In this case, Marcel used the Join-Path cmdlet to create a new path. He appended the remainder of the file path to a new path header, D:\Archive:

 

PS C:\Windows> join-path -path <NewPath> -Childpath <path-substring>

 

PS C:\Windows> join-path -path D:\Archive -childpath (get-item C:\Windows\Dev\tmp.txt).FullName.SubString($pwd.path.length)

 

D:\Archive\Dev\tmp.txt

 

This was a great solution for this task, but it’s also a great strategy for many different tasks. It’s a neat twist on Split-Path.

 

June Blender [MSFT]

Windows PowerShell Documentation

1 comment

Comments are closed. Login to edit/delete your existing comments

  • Avatar
    Eric MarquezMicrosoft employee

    I’ve been trying to figure out how to find the file path that has the correct case.  I know windows is case insensitive but I’m working with WSL and this is case sensitive.  I need to know what is the actual case used by windows in a path.example: If I open explorer the actual path with the correct case is C:\Tools\CredentialProviderbut in PowerShell, if I use (Get-Item C:\Tools\credentialprovider\).FullName or (Get-ChildItem -Path C:\Tools\credentialprovider\).FullName I will get.C:\Tools\credentialprovider\which is not the correct case.  At a point in time (Get-Item C:\Tools\credentialprovider\).Target used to provide the correct path but now I get a null result.  Is there a way to get the actual path in PowerShell?I tried this in PowerShell 5.1.17763.316 and 6.1.2.