0

Using PowerShell, I'm trying to pipe a file list from my local drive so Copy-Items can copy them to a network folder. The following statement does find the files I need, and copies them. However, all path information is lost and they all end up in the same folder. I want each file to be saved in relative path fashion at the destination with its file path from the local disk.

So for example "c:\users\user_name\Documents\file_1.txt" should be copied to "\\server\share\my_folder\users\user_name\Documents\file_1.txt"

My searches have proved fruitless. Can this statement be modified to achieve that?

Get-ChildItem -Path c:\users\user_name -r | ? {!($_.psiscontainer) -AND $_.lastwritetime -gt (get-date).date} | %{Copy-Item -Path $_.fullname -destination "\\server\share\my_folder" -Recurse -Force -Container}

EDIT:

It seems like Copy-Item should be able to do this. The following command works:

Copy-Item -Path c:\users\user_name\Documents -Destination "\\server\share\my_folder" -recurse -Force

This command copies all files, sub-folders and files in the sub-folders found in and under c:\users\user_name\Documents. It then recreates the relative directory structure on the network share. It seems like the $_.fullname parameter from the first command is not being treated in a similar fashion. I'm most certainly not piping file list in the expected manner to Copy-Item. Any other advice?

3
  • I would recommend you take a look at robocopy instead Commented Aug 18, 2020 at 21:31
  • Does this answer your question? Get relative path of files in sub-folders from the current directory Commented Aug 19, 2020 at 10:11
  • @iRon - That does not answer my question. I have the relative path info, I'm trying to recreate that structure on the network file share. Below Doug Maurer indicates there is no way for the Copy-Item command to accomplish that. So, I guess I have to roll my own functionality that was present in MS-DOS 6.0. Disappointing. Commented Aug 19, 2020 at 12:55

3 Answers 3

0

There are a few things we should go over. First, If you just want files you can drop the !$_.psiscontainer check and just add -file to the Get-Childitem command. Next, if you intend to copy files that are older that Get-Date, the comparison needs to be -lt. As for creating the directory structure in the destination, you'd need to make it yourself. You can achieve it by stripping c: off the path and using the *-Path cmdlets like this.

$source      = 'c:\users\user_name\Documents'
$destination = '\\server\share\my_folder'

Get-ChildItem -Path $source -File -Recurse |
    Where-Object {$_.lastwritetime -lt (get-date).date} |
        ForEach-Object {
            $destdir = Join-Path $destination (Split-Path $_.FullName -Parent).Substring(2)
            
            If(-not(Test-Path $destdir))
            {
                Write-Host Making directory $destdir -ForegroundColor Cyan
                $null = New-Item -Path $destdir -ItemType Directory
            }
            
            Write-Host Copying file $_.name to $destdir -ForegroundColor Green
            Copy-Item $_.FullName -Destination $destdir
        }

However as Bill_Stewart said robocopy or another tool may be a better fit. Robocopy would surely be much faster so if you need this to process a lot of data, I'd direct my effort in that direction.

Sign up to request clarification or add additional context in comments.

4 Comments

No, I don't want files older than Get-Date. What about my question suggested that my boolean comparison was opposite of what I wrote? My question indicated that I was returning the files I was after AND copying them. They were just not being copied as I had hoped. I included !$_.psiscontainer since I'm trying to copy files and folders. So, taking the implication of your re-worked question and Bill_Stewart's comment from above, the answer to my question appears to be "No". The Copy-Item portion of my statement can't be reworked to re-create the file paths.
So you want files made in the future that don’t exist yet. Also, !$_.psiscontainer means not a directory so you’re filtering those out.
1) This will be run at the end of the day and pick up files that were created/updated the same day. I'm trying to create an incremental back-up strategy. As I say, I'm finding the files I need. 2) Not a directory - I don't want to pipe an updated directory to Copy-Item since that will result in a full directory copy. This command works in that it lists the files I want to copy: Get-ChildItem -Path c:\users\tlash -r | ? {!($_.psiscontainer) -AND $_.lastwritetime -gt (get-date).date}. I just can't get the path to copy.
I do believe you’re correct about copy item. If you were doing a folder recursively it would create the sub directories, but for each file I don’t know of a way other than what I’ve suggested.
0

As mentioned in the noted using Set-Location (see: Get relative path of files in sub-folders from the current directory):

Set-Location c:\users\user_name
Get-ChildItem -Recurse |
  Where-Object { !($_.psiscontainer) -AND $_.lastwritetime -gt (get-date).date } |
    Foreach-Object {
        $Destination = Join-Path \\server\share\my_folder ("$_" | Resolve-Path -Relative)
        New-Item -ItemType File -Path $destination -Force
        Copy-Item -Path "$_" -Destination $Destination -Force
    }

5 Comments

that one-liner did not work. It does return a proper file list, but the Copy-Item component still fails: "ObjectNotFound: (c:\users\user_name\test.txt:string) [Resilve-Path], ItemNotFoundException. test.txt is in Documents.
Sorry, you will need to remove the last -Recurse and -Container switches as that is already defined by the Get-ChildItem and Where-Object cmdlets (I have update the answer).
Same error. When I use this == Set-Location c:\users\user_name; Get-ChildItem -Recurse | ? { !($_.psiscontainer) -AND $_.lastwritetime -gt (get-date).date } | Resolve-Path -Relative == I get: .\Documents\test.txt. However, the Copy-Item is still trying to copy c:\users\user_name\test.txt which does not exist. Copy-Items can't be made to recognize the path being passed.
Okay, this works - Set-Location c:\users\user_name; Get-ChildItem -Recurse | ? { !($_.psiscontainer) -AND $_.lastwritetime -gt (get-date).date } | % {Copy-Item $_.fullname -destination (Join-Path '\\server\share\my_folder' ($_ | Resolve-Path -Relative)) -Force } BUT ONLY IF THE DESTINATION FOLDER "Documents" ALREADY EXISTS. So close!
This "folder" issue is in fact described at Should Copy-Item create the destination directory structure?. I incorporated this in the answer. I did removed the oneliner as is no longer clear what it does but you might simply concatenate the statements with a semicolon (;) if you do need a one liner (you can also just paste multiple lines into your IDE).
0

Okay, this was quite the challenge, but I finally found a mechanism. Thanks to @iRob for pointing me to a potential solution. I could not get his code to create the relative paths at the destination, but was able to extend his concept. I ended up adding in a New-Item command (found via Powershell 2 copy-item which creates a folder if doesn't exist) and a Split-Path (found here Copy a list of files to a directory). The Split-Path sliced off the file name from the joined path, then that path was created via the New-Item command. Finally the Copy-Item had a place to copy the file. So in that sense @Doug Maurer was also right that Copy-Item would not do this itself. Some extra preparation was need before the Copy-Item command could successfully run.

Set-Location c:\users\user_name; Get-ChildItem -Recurse | ? { !($_.psiscontainer) -AND $_.lastwritetime -gt (get-date).date } | % {Copy-Item $_.fullname -destination (New-Item -type directory -force ((Join-Path '\\server\share\my_folder' ($_ | Resolve-Path -Relative)) | Split-Path -Parent)) -Force}

IMHO something that was trivial in MS-DOS 6.0 (xcopy /D:08-20-2020 /S c:\users\user_name*.* \server\share\my_folder) should not be so difficult in PowerShell. Seems like there should be an easier way. Now that I think of it, probably should have stuck to xcopy instead of diving down this rabbit hole. PowerShell just seems so shiny.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.