Home > database >  Copying files to directory whilst retaining directory structure from list
Copying files to directory whilst retaining directory structure from list

Time:04-08

Good afternoon all,

I'm guessing this is super easy but really annoying for me; I have a text file with a list of files, in the same folders there are LOTS of other files but I only need specific ones.

$Filelocs = get-content "C:\Users\me\Desktop\tomove\Code\locations.txt"

Foreach ($Loc in $Filelocs){xcopy.exe $loc C:\Redacted\output /s }

I figured this would go through the list which is like

"C:\redacted\Policies\IT\Retracted Documents\Policy_Control0.docx"

and then move and create the folder structure in a new place and then copy the file, it doesn't.

Any help would be appreciated.

Thanks RGE

CodePudding user response:

xcopy can't know the folder structure when you explicitly pass source file path instead of a source directory. In a path like C:\foo\bar\baz.txt the base directory could be any of C:\, C:\foo\ or C:\foo\bar\.

When working with a path list, you have to build the destination directory structure yourself. Resolve paths from text file to relative paths, join with destination directory, create parent directory of file and finally use PowerShell's own Copy-Item command to copy the file.

$Filelocs = Get-Content 'locations.txt'

# Base directory common to all paths specified in "locations.txt"
$CommonInputDir = 'C:\redacted\Policies'

# Where files shall be copied to
$Destination = 'C:\Redacted\output'

# Temporarily change current directory -> base directory for Resolve-Path -Relative
Push-Location $CommonInputDir

Foreach ($Loc in $Filelocs) {

    # Resolve input path relative to $CommonInputDir (current directory)  
    $RelativePath = Resolve-Path $Loc -Relative

    # Resolve full target file path and directory
    $TargetPath   = Join-Path $Destination $RelativePath
    $TargetDir    = Split-Path $TargetPath -Parent

    # Create target dir if not already exists (-Force) because Copy-Item fails 
    # if directory does not exist.
    $null = New-Item $TargetDir -ItemType Directory -Force

    # Well, copy the file
    Copy-Item -Path $loc -Destination $TargetPath
}

# Restore current directory that has been changed by Push-Location
Pop-Location

Possible improvements, left as an exercise:

  • Automatically determine common base directory of files specified in "locations.txt". Not trivial but not too difficult.
  • Make the code exception-safe. Wrap everything between Push-Location and Pop-Location in a try{} block and move Pop-Location into the finally{} block so the current directory will be restored even when a script-terminating error occurs. See about_Try Catch_Finally.
  • Related