Move Items relative to current location in Powershell - powershell

I need to traverse a folder (ParentLogFolder), find logs and move them from the relative folder (SUBLOG) to a subfolder (CompressedLogFolder)
ParentLogfolder
---SubLog
------CompressedLogFolder
---SubLog
------CompressedLogFolder
Folder structure, not limited to 2 SubLogfolders, can be 100+
$Path = "C:\ParentLogFolder"
$Pattern = "*.log"
$date = get-date
$AgeLimit = $date.AddDays(-31)
Get-ChildItem -Filter $Pattern -path $path -recurse | Where-Object
{$_.LastWriteTime -lt $AgeLimit} | Move-Item -Path
I can't figure out how to get the files of the parent folder into a variable and add the subfolder as destination in the Move-Item part.
Anyone out there, who can help answer this?
Don't mind the $Date part of the code, it works, it's just there because it needs to be in the solution.

Instead of onliner, consider emphasis on code clarity. That is, break the process in substeps by divide and conquer approach. It's easier to check variables for incorrect values when the pipeline isn't used too much. Like so,
$allFiles = Get-ChildItem -Filter $Pattern -path $path -recurse
$oldFiles = $allFiles | ? { $_.LastWriteTime -lt $ageLimit }
foreach($file in $oldFiles) {
$archiveFolder = join-path $file.DirectoryName 'someArchiveFolder'
$destination = join-path $archiveFolder $file.Name
move-item -whatif $file.FullName $destination
}
The -whatif switch will print what the move command would do. Remove it to actually move files.

Related

Excluding Folders with Get-ChildItem - Need Help Debugging a Script

I've searched through both StackOverflow and SuperUser to try to figure this out, and I'm still getting plagued by a problem I can't figure out how to fix. I know it's something simple, but after playing with it for an hour I'm still stumped. Simple question: how the heck do I tell Get-Childitem to exclude folders?
Right up front here's the code that doesn't work:
$sourceDir="E:\Deep Storage"
$targetDir="W:\Deep Storage"
$excludeThese = 'Projects2','Projects3','Projects4';
Get-ChildItem -Path $sourceDir -Directory -Recurse |
where {$_.fullname -notin $excludeThese} |
Get-ChildItem -Path $sourceDir | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Move-Item -Destination $nextName -Force -Verbose -WhatIf
}
}
The underlying concept here already works:
$sourceDir="E:\Deep Storage"
$targetDir="W:\Deep Storage"
Get-ChildItem -Path $sourceDir -File -Recurse | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Copy-Item -Destination $nextName -Verbose
}
Basically what this does is it moves folders from one place to another, and if files exist in both places, it renames files coming in. It helps keep my archive drive clear. But there are three folders there that I want to exclude because I still pull assets from them regularly, so I don't need those files moved.
Hence the difference between the two code samples: in the first one, I'm trying to get Get-Childitem to exclude a specific trio of folders, while this second one just grabs everything all at once.
I tried just doing a straight -Exclude with $excludeThese as the variable, without success; I tried skipping the variable approach altogether and just putting the folder names in after -Exclude. Still didn't work. I also tried putting in the entire path to the folders I wanted to exclude. No good--no matter what I did, the -WhatIf showed that the script was trying to move everything, including the folders I was theoretically excluding.
The last trick I tried was one I came across here on SO, and that was to go a gci with the exclude argument first, then do another gci after it. That still failed, so now I have to turn to the experts for help.
I would use a regex string created from the (escaped) directory names to exclude to make sure files withing these folders are ignored.
Also, by using a lookup Hashtable of all file names already present in the target folder, figuring out if a file with a certain name already exists is extremely fast.
$sourceDir = 'E:\Deep Storage'
$targetDir = 'W:\Deep Storage'
$excludeThese = 'Projects2','Projects3','Projects4';
# create a regex string with all folder names to exclude combined with regex OR (|)
$excludeDirs = ($excludeThese | ForEach-Object { [Regex]::Escape($_) }) -join '|'
# create a lookup Hashtable and store the filenames already present in the destination folder
$existingFiles = #{}
Get-ChildItem -Path $targetDir -File | ForEach-Object { $existingFiles[$_.Name] = $true }
Get-ChildItem -Path $sourceDir -File -Recurse |
Where-Object {$_.DirectoryName -notmatch $excludeDirs} |
ForEach-Object {
# construct the new filename by appending an index number if need be
$newName = $_.Name
$count = 1
while ($existingFiles.ContainsKey($newName)) {
$newName = "{0}_{1}{2}" -f $_.BaseName, $count++, $_.Extension
}
# add this new name to the Hashtable so it exists in the next run
$existingFiles[$newName] = $true
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $targetDir -ChildPath $newName
$_ | Move-Item -Destination $newFile -Force -Verbose -WhatIf
}
Assuming the excluded directories are at the top:
$sourceDir="E:\Deep Storage"
$excludeThese = 'Projects2','Projects3','Projects4'
get-childitem $sourcedir -exclude $excludethese | get-childitem -recurse

Moving Files based on filename

Im looking to move files based on the last half of the filename. Files look like this
43145123_Stuff.zip
14353135_Stuff.zip
2t53542y_Stuff.zip
422yg3hh_things.zip
I am only looking to move files that end in Stuff.zip
I have this in PowerShell so far but it only will move files according to the first half of a file name.
#set Source and Destination folder location
$srcpath = "C:\Powershelltest\Source"
$dstpath = "C:\Powershelltest\Destination"
#Set the files name which need to move to destination folder
$filterLists = #("stuff.txt","things")
#Get all the child file list with source folder
$fileList = Get-ChildItem -Path $srcpath -Force -Recurse
#loop the source folder files to find the match
foreach ($file in $fileList)
{
#checking the match with filterlist
foreach($filelist in $filterLists)
{
#$key = $file.BaseName.Substring(0,8)
#Spliting value before "-" for matching with filterlists value
$splitFileName = $file.BaseName.Substring(0, $file.BaseName.IndexOf('-'))
if ($splitFileName -in $filelist)
{
$fileName = $file.Name
Move-Item -Path $($file.FullName) -Destination $dstpath
}
}
}
There seems to be some differences between the state goal and what the code actually does. This will move the files to the destination directory. When you are confident that the files will be moved correctly, remove the -WhatIf from the Move-Item command.
$srcpath = "C:\Powershelltest\Source"
$dstpath = "C:\Powershelltest\Destination"
Get-ChildItem -File -Recurse -Path $srcpath |
ForEach-Object {
if ($_.Name -match '.*Stuff.zip$') {
Move-Item -Path $_.FullName -Destination $dstpath -WhatIf
}
}
Actually this can be written in PowerShell very efficiently (I hope I got the details right, let me know):
Get-ChildItem $srcpath -File -Force -Recurse |
where { ($_.Name -split "_" | select -last 1) -in $filterLists } |
Move-Item $dstpath
Alternatively, if you only want to look for this one particular filter, you can specify that directly, using wildcards:
Get-ChildItem $srcpath -Filter "*_Stuff.zip"

ForEach-Object to look in subfolders

I Have this powershell script “copFiles.ps1” that looks in a txt file "Filestocopy.txt" for a list and copies them to a destination
$source = "C:\Data\Filestocopy.txt"
$destination = "C:\Data\Models"
Get-Content $source | ForEach-Object {copy-item $_ $destination}
It’ll only copy the files if they’re in the same folder as the .ps1 file and it ignores subfolders, how can I get it to look in subfolders of the folder that its in, I gather I need to use the -recurse option but don’t know how to rewrite it so it works.
The .ps1 file is fired by a bat file.
Many thanks
I don't know how fast this will be, but you can give an array as the argument for the -Path parameter of Get-ChildItem add the -Recurse switch to dig out the files in subdirectories and simply pipe them along to Copy-Item. something like:
Get-ChildItem (Get-Content $Source) -Recurse |
Copy-Item -Destination $destination
You may also want to add the -File switch.
Update
Based on your comment I played around with this a a little more:
$source = "C:\Data\Filestocopy.txt"
$Destination = "C:\data\Models"
# Get-ChildItem (Get-Content $Source) -Recurse |
Get-ChildItem (Get-Content $Source) -Recurse -File |
ForEach-Object{
If( $_.Directory.FullName -eq $Destination )
{ # Don't work on files already present in the destination
# when the destination is under the current directory...
Continue
}
$FileNum = $null
$NewName = Join-Path -Path $Destination -ChildPath $_.Name
While( (Test-Path $NewName) )
{
++$FileNum
$NewName = Join-Path -Path $Destination -ChildPath ($_.BaseName + "_" + $FileNum + $_.Extension)
}
Copy-Item $_.FullName -Destination $NewName
}
This will increment the destination file name in cases where a file by that name already exists in the destination. If the destination is under the current directory it will prevent analyzing those files by comparing the path of the file to the destination. Files must have unique names in a given folder so I'm not sure how else it can be handled.

Move Files From One Location To Another And Exclude Subfolders

I want to move files from one location to another but not to move any files from subfolders in the directory I'm moving the files from.
I have the following code but this moves files in subfolders. Also, the extensions should only be .xml and .pdf.
This script moves all files from $MoveThese into the Destination, even the files in subfolders e.g. C:\Test\Folder1\Subfolder. Any suggestions would be helpful.
$MoveThese = "C:\Test\Folder1", "C:\Test2\Folder2", "C:\Test3\Folder3"
$Destination = "C:\Test\Docs", "C:\Test2\Docs", "C:\Test3\Docs"
For($i=0; $i -lt $FailFolders.Length; $i++){
Get-ChildItem -Path $MoveThese[$i] -Recurse -Include "*.xml", "*.pdf" | Move-Item -Destination $Destination[$i]
}
If you don't want files in subfolders, don't use -Recurse. As per Get-ChildItem documentation this "Gets the items in the specified locations and in all child items of the locations."
Missed this part of the -Include statement:
The -Include parameter is effective only when the command includes the
-Recurse parameter
Solution:
Get-ChildItem $MoveThese[$i] -File |
Where-Object {$_.Extension -in #(".xml",".pdf")} |
Move-Item -Destination $Destination[$i]
Include does not work without the -Recurse switch unfortunately unless the path uses a wildcard like in C:\Windows*
This might do it for you:
$MoveThese = "C:\Test\Folder1", "C:\Test2\Folder2", "C:\Test3\Folder3"
$Destination = "C:\Test\Docs", "C:\Test2\Docs", "C:\Test3\Docs"
For($i=0; $i -lt $MoveThese.Length; $i++){
Get-ChildItem $MoveThese[$i] -File |
Where-Object { $_.Name -like "*.xml" -or $_.Name -like "*.pdf" |
Move-Item -Destination $Destination[$i] -Force
}

How to find the path of the current object in a for-eachobject loop

I am trying to find the path of the object currently being operated on in a for-eachobject loop. Can someone explain how to do this? I am trying to set the path of the source for Copy-Item to whatever path the file is on. This is the first project I have worked with Powershell on. Assume $Src has been properly instantiated.
Get-ChildItem -Path $Src -Recurse -Force | Where-Object {$_.Name -notmatch "Archive" -and ($_.PSIsContainer)}|
ForEach-Object {
$DateStr = $_.BaseName.Substring(0,2)+'/'+$_.BaseName.Substring(3,2)+'/'+$_.BaseName.Substring(6,4)
$FileDate = get-date $DateStr
If ( $FileDate -ge $Date -and !($_.PSIsContainer)) {
$ParentOjbect = $_.Directory
$Parent = $ParentOjbect.Name
Copy-Item -Path (Join-Path -Path $Src -ChildPath '\*.txt' ) #this
#needs to be corrected to only point to the current file
-Dest (Join-Path $Dst ("$($_.Directory.Name) $($_.Name)")) -WhatIf
}
}
Copy-Item -Path (Join-Path -Path $_.FullName -ChildPath '\*.txt' )
I suggest you also use -WhatIf on that also to check it does what you expect first.
$_, or $PSItem (PowerShell 3.0+), accesses the current object in the pipeline. From here, you can access its members.