Fun with Paths

Fun with Paths

  • Comments 4

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

Leave a Comment
  • Please add 6 and 7 and type the answer here:
  • Post
  • The next command will give you all available aliases:

        Get-Command -CommandType Alias

  • Nice article for a newbie like me.

    Although when I tried this using a UNC path their is a problem. $pwd doesnt just return the current UNC path but instead returns

    Microsoft.PowerShell.Core\FileSystem::\\ServerName\ShareName

    However the FullName property returns just

    \\ServerName\ShareName\FileName

    As such the $pwd.path.length is incorrect when executing

    $_.FullName.SubString($pwd.path.length)

    As this also will count the length of

    Microsoft.PowerShell.Core\FileSystem::

  • You can eliminate the current dir from the path by calling split-path -parent twice.

    Example:

      $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path

      $ScriptParentDir = Split-Path -Parent $ScriptDir

    $MyInvocation.MyCommand.Path returns the path to the script file.

    If you just need the parent directory you can shorten it to the following:

      $ScriptParentDir = Split-Path -Parent ( Split-Path -Parent $MyInvocation.MyCommand.Path )

  • I found a instance that I need to find the filename from the absolute path that was not a local path. In this case I found I can use "Split" resolve the filename.

    ##Example:

    $strName = "D:\Microsoft SQL Server\MSSQL.1\MSSQL\ABC_DBA.mdf"

    $filename = $strName.split("\")[(($strName.split("\").count)-1)]

    ##To break this down and explain...(but note only 2 lines are needed to get the outcome)

    $strName = "D:\Microsoft SQL Server\MSSQL.1\MSSQL\ABC_DBA.mdf"

    ##get the count of splits (This returns "5")

    $strName.split("\").count

    ##this would be the manual way to return the exact part of the array that gives us the filename

    $strName.split("\")[4]

    ##We will now insert the count(5) and subtract 1 in place of the 4 (because the array begins with 0, not 1)

    $strName.split("\")[(($strName.split("\").count)-1)]

    ##lastly i assign the above line into a variable called filename

    $filename = $strName.split("\")[(($strName.split("\").count)-1)]

Page 1 of 1 (4 items)