?
Solved

Using Powershell, Move files while retaining folder structure

Posted on 2011-10-04
8
Medium Priority
?
4,951 Views
Last Modified: 2012-05-12
I'm trying to write a powershell script that will move any files that havn't been access in over Xdays to an archive location.

I was trying to follow along with this: http://blogs.msdn.com/b/powershell/archive/2007/04/27/fun-with-paths.aspx

I can get individual parts of it to work but when i try to move the files it fails.

this is my script:
$Source="c:\TEST\Source"
$Target="c:\TEST\Target"
$days="365"
$Now=Get-Date
$LastAccess =$Now.AddDays(-$days)
$Files=get-childitem $Source -include *.* -recurse | Where {$_.LastAccess -le “$LastAccess”} 

CD $Source

foreach ($File in $Files)
{Move-Item -destination {join-path $Target $_.FullName.SubString($pwd.path.length)}}

Open in new window


This is the error i get when i run it:
Move-Item : Cannot evaluate parameter 'Destination' because its argument is specified as a script block and there is no inpu
t. A script block cannot be evaluated without input.
At C:\Scripts\MoveOldFiles.ps1:11 char:24
+ {Move-Item -destination <<<<  {join-path $Target $_.FullName.SubString($pwd.path.length)}}
    + CategoryInfo          : MetadataError: (:) [Move-Item], ParameterBindingException
    + FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.MoveItemCommand
 
Move-Item : Cannot evaluate parameter 'Destination' because its argument is specified as a script block and there is no inpu
t. A script block cannot be evaluated without input.
At C:\Scripts\MoveOldFiles.ps1:11 char:24
+ {Move-Item -destination <<<<  {join-path $Target $_.FullName.SubString($pwd.path.length)}}
    + CategoryInfo          : MetadataError: (:) [Move-Item], ParameterBindingException
    + FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.MoveItemCommand
 
Move-Item : Cannot evaluate parameter 'Destination' because its argument is specified as a script block and there is no inpu
t. A script block cannot be evaluated without input.
At C:\Scripts\MoveOldFiles.ps1:11 char:24
+ {Move-Item -destination <<<<  {join-path $Target $_.FullName.SubString($pwd.path.length)}}
    + CategoryInfo          : MetadataError: (:) [Move-Item], ParameterBindingException
    + FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.MoveItemCommand

Open in new window

0
Comment
Question by:SladeHD
  • 4
  • 3
8 Comments
 
LVL 16

Assisted Solution

by:Dale Harris
Dale Harris earned 200 total points
ID: 36910174
You're going to want to encase your -Destination with this:

{Move-Item -destination $(join-path $Target $_.FullName.SubString($pwd.path.length))}

Also, Move-Item can give you errors if you go between devices, like your local computer to a network share, etc.  So most of us recommend you actually copy them first, then remove them after.

HTH,

Dale Harris
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 36910180
Use subexpressions instead of a script block as destination. Further I recommend to pipe the result to move-item, so both the get-childitem and the move-item can work in parallel, instead of having gci finishing before performing the moves. See this improved code:
Set-StrictMode -Version Latest
$Source="c:\TEST\Source"
$Target="c:\TEST\Target"
$days="365"
$LastAccess =(get-date).AddDays(-$days)
get-childitem $Source -include *.* -recurse `
| ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} `
| % {Move-Item -WhatIf $_.FullName -destination (join-path $Target $_.FullName.SubString($Source.length))}

Open in new window

Or, without using foreach-object (%), and instead having a code block in move-item:
Set-StrictMode -Version Latest
$Source="c:\TEST\Source"
$Target="c:\TEST\Target"
$days="365"
$LastAccess =(get-date).AddDays(-$days)
get-childitem $Source -include *.* -recurse `
| ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} `
| Move-Item -WhatIf -destination {join-path $Target $_.FullName.SubString($Source.length)}

Open in new window

Remove the -whatif in both snippets to actually perform the move.
0
 

Author Comment

by:SladeHD
ID: 36911243
Qlemo, Using your code (either one, with the -WhatIf removed) i get the following error:
Move-Item : Could not find a part of the path.
At C:\Scripts\MoveOldFiles.ps1:30 char:124
+ get-childitem $Source -include *.* -recurse | ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} | % {Move-Item <<<<  $_.FullName -destination (join-path $Target $_
.FullName.SubString($Source.length))}
    + CategoryInfo          : WriteError: (C:\TEST\Source\Files\TESTFILE1.txt:FileInfo) [Move-Item], DirectoryNotFoundException
    + FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
 
Move-Item : Could not find a part of the path.
At C:\Scripts\MoveOldFiles.ps1:30 char:124
+ get-childitem $Source -include *.* -recurse | ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} | % {Move-Item <<<<  $_.FullName -destination (join-path $Target $_
.FullName.SubString($Source.length))}
    + CategoryInfo          : WriteError: (C:\TEST\Source\Files\TESTFILE2.txt:FileInfo) [Move-Item], DirectoryNotFoundException
    + FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
 
Move-Item : Could not find a part of the path.
At C:\Scripts\MoveOldFiles.ps1:30 char:124
+ get-childitem $Source -include *.* -recurse | ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} | % {Move-Item <<<<  $_.FullName -destination (join-path $Target $_
.FullName.SubString($Source.length))}
    + CategoryInfo          : WriteError: (C:\TEST\Source\Files\TESTFILE3.txt:FileInfo) [Move-Item], DirectoryNotFoundException
    + FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand

Open in new window


However, when i leave the -WhatIf the output looks good:
What if: Performing operation "Move File" on Target "Item: C:\TEST\Source\Files\TESTFILE1.txt Destination: C:\TEST\Target\Fil
es\TESTFILE1.txt".
What if: Performing operation "Move File" on Target "Item: C:\TEST\Source\Files\TESTFILE2.txt Destination: C:\TEST\Target\Fil
es\TESTFILE2.txt".
What if: Performing operation "Move File" on Target "Item: C:\TEST\Source\Files\TESTFILE3.txt Destination: C:\TEST\Target\Fil
es\TESTFILE3.txt".

Open in new window

0
Get your Disaster Recovery as a Service basics

Disaster Recovery as a Service is one go-to solution that revolutionizes DR planning. Implementing DRaaS could be an efficient process, easily accessible to non-DR experts. Learn about monitoring, testing, executing failovers and failbacks to ensure a "healthy" DR environment.

 
LVL 71

Expert Comment

by:Qlemo
ID: 36911484
Stupid me. Of course you need to create the folder structure first - move-item will not do that for you. So we have to use the first version:
Set-StrictMode -Version Latest
$Source="c:\TEST\Source"
$Target="c:\TEST\Target"
$days="365"
$LastAccess =(get-date).AddDays(-$days)
get-childitem $Source -include *.* -recurse `
| ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} `
| % {
  $newpath = join-path $Target $_.FullName.SubString($Source.length)
  New-Item $newpath -type directory -ErrorAction SilentlyContinue
  Move-Item -WhatIf $_.FullName -destination $newpath
}

Open in new window

0
 

Author Comment

by:SladeHD
ID: 36911681
That works but it creates a directory for every file, for example

if the Source is C:\Test\Source\ Files\File1.txt
and the Target is C:\Test\Target\

it should create C:\Test\Target\ Files\File1.txt

but instead it is doing this: C:\Test\Target\ Files\File1.txt\File1.txt
it is making a directory for every file.

any ideas on how i could get around that?
0
 
LVL 71

Accepted Solution

by:
Qlemo earned 1800 total points
ID: 36911870
Should have tested that :(.
Set-StrictMode -Version Latest
$Source="c:\TEST\Source"
$Target="c:\TEST\Target"
$days="365"
$LastAccess =(get-date).AddDays(-$days)
get-childitem $Source -include *.* -recurse `
| ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} `
| % {
  $newpath = $Target + $_.DirectoryName.Replace($Source,"")
  New-Item $newpath -type directory -ErrorAction SilentlyContinue
  Move-Item -WhatIf $_.FullName -destination $newpath
}

Open in new window

0
 

Author Comment

by:SladeHD
ID: 36920587
this is what i ended up with, its working great, thanks!

Set-StrictMode -Version Latest
$Source="H:\"
$Target="E:\H Drive Archive"
$days="365"
$LastAccess =(get-date).AddDays(-$days)
get-childitem $Source -include *.* -recurse | ? {!$_.PsIsContainer -and $_.LastAccessTime -le “$LastAccess”} | % {
  $newpath = join-path $Target $_.DirectoryName.SubString($Source.length)
  New-Item $newpath -type directory -ErrorAction SilentlyContinue
  Move-Item $_.FullName -destination $newpath

Open in new window

0
 

Author Closing Comment

by:SladeHD
ID: 36920609
None of the answers provided worked as they were, i had to use pieces of them and put something together.
0

Featured Post

Making Bulk Changes to Active Directory

Watch this video to see how easy it is to make mass changes to Active Directory from an external text file without using complicated scripts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The Nano Server Image Builder helps you create a custom Nano Server image and bootable USB media with the aid of a graphical interface. Based on the inputs you provide, it generates images for deployment and creates reusable PowerShell scripts that …
My attempt to use PowerShell and other great resources found online to simplify the deployment of Office 365 ProPlus client components to any workstation that needs it, regardless of existing Office components that may be needing attention.
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…
Screencast - Getting to Know the Pipeline

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question