Fun with Paths

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