How to move files based on filename using PowerShell? - powershell

I am new to PowerShell and am stuck at something.
How to move files based on filename using PowerShell?
Move 0001_ddmmyy_username[active1]_L_kkkkk.pdf to C:\Users\abc\London
Move 0001_ddmmyy_jacky[active1]_R_kkkkk.pdf to C:\Users\abc\Russia
Move 0001_ddmmyy_jim[active1]_P_kkkkk.pdf to C:\Users\abc\Poland
I used the following code to move files to a folder called London. It fails if the file name has any square bracket with text [kkkk].
gci 'C:\Users\abc' -Recurse -Include "*_L*" | %{ Move-Item $_.FullName 'C:\Users\abc\London'}
How can I automate this?

There are multiple ways:
Get-Location
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-location?view=powershell-7
gci (Get-Location).Path
or simply .\
gci .\
additionaly, gci will default to current path if none is specified. So the following will get the current folder:
gci
Use -LiteralPath in the Move-Item cmdlet.
Move-Item -LiteralPath $sourcePath $destinationPath
I'd do something similar to below:
# Note I'm using -File so it only returns files
$items = gci .\ -Recurse -File -Include *_L*, *_R*, *_P*
foreach($item in $items) {
# Could utilize something cleaner/better but this will do
if($item.Name -like "*_L*") {
Move-Item -LiteralPath $item 'C:\Users\abc\London'
} elseif($item.Name -like "*_R*") {
Move-Item -LiteralPath $item 'C:\Users\abc\Russia'
} elseif ($item.Name -like "*_P*") {
Move-Item -LiteralPath $item 'C:\Users\abc\Poland'
} else {
# Unexpected output
Write-Host "The path did not match what was expected"
Write-Host $item.Name
}
}

Windows 10 64-bit. PowerShell 5.
How to move files based on file basename using PowerShell gci, foreach, if, -like, move-item and -literalpath
Move 0001_ddmmyy_username[active1]_L_kkkkk.pdf to C:\Users\abc\London
Move 0001_ddmmyy_jacky[active1]_R_kkkkk.pdf to C:\Users\abc\Russia
Move 0001_ddmmyy_jim[active1]_P_kkkkk.pdf to C:\Users\abc\Poland
Change $env:userprofile\desktop. Remove -whatif when you are satisfied it works.
$items = gci -recurse -file *_L_*, *_R_*, *_P_*
foreach($item in $items) {
# Change $env:userprofile\desktop
if($item -like "*_L_*") {
Move-Item -LiteralPath $item "$env:userprofile\desktop\London" -whatif
} elseif($item -like "*_R_*") {
Move-Item -LiteralPath $item "$env:userprofile\desktop\Russia" -whatif
} elseif ($item -like '*_P_*') {
Move-Item -LiteralPath $item "$env:userprofile\desktop\Poland" -whatif
}
}
Comments:
With some cmdlets you will get odd results when you use -Path and any part of the full file name contains []. The fix is to use the -LiteralPath parameter. See Get-Help Move-Item -Parameter *path* – Lee_Dailey
How to move files based on file basename using PowerShell gci, foreach, if, -like, move-item and -literalpath

Related

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"

Copy-Item doesn't work with square bracket in destination parameter [duplicate]

I am trying to use PowerShell (v.1) to copy over only files that match a pattern. The file naming convention is:
Daily_Reviews[0001-0871].journal
Daily_Reviews[1002-9887].journal
[...]
When I run it, the method "Copy-Item" complains:
Dynamic parameters for cmdlet cannot be retrieved. The specified wildcard pattern is not valid: Daily_Reviews[0001-0871].journal
+ Copy-Item <<<< $sourcefile $destination
The error is due to the "[" and "]" in the file names. When I remove the left and right brackets, it works as expected. But looks like PowerShell 1 doesn't have the -LiteralPath flag so is there another way to get Copy-Item to work in PowerShell 1 with file names that contain brackets?
$source = "C:\Users\Tom\"
$destination ="C:\Users\Tom\Processed\"
if(-not(Test-Path $destination)){mkdir $destination | out-null}
ForEach ($sourcefile In $(Get-ChildItem $source | Where-Object { $_.Name -match "Daily_Reviews\[\d\d\d\d-\d\d\d\d\].journal" }))
{
Copy-Item $sourcefile $destination
}
Well, after researching this more I found a workaround:
$src = [Management.Automation.WildcardPattern]::Escape($sourcefile)
Copy-Item $src $destination
$_ references the currently-referenced argument; you can't use it the way that you are here because that's outside the pipeline.
$source = "C:\Users\Tom\"
$destination ="C:\Users\Tom\Processed\"
if(-not(Test-Path $destination)){mkdir $destination | out-null}
ForEach ($sourcefile In $(Get-ChildItem $source | Where-Object { $_.Name -match "Daily_Reviews\[\d\d\d\d-\d\d\d\d\].journal" }))
{
Copy-Item -literalpath $sourcefile $destination
}

Work around for writing Destination argument as a script block in powershell

My organization has a lot of picture files in different folders all over the network.
I've been tying to consolidate them in a "PictureLibrary" folder by the project folder they're in.
Since there are other files besides picture files in these project folders I can't just move the whole folder.
I've been trying the code below:
($Images = gci "Z:\DivisionFolder" -Recurse -file -Include "*.jpg") |
Foreach-object {
copy-Item $_.DirectoryName -Destination "Z:\DivisionFolder\PictureLibrary"
}
$a=0
For ($a; $a -lt $Images.count; $a++){
move-item $Images[$a].fullname -Destination {join-path -Path "D:\(1) PROJECTS & PORTFOLIOS\PictureLibrary" -ChildPath $Images[$a].Directoryname}
}
I've tried it in various formats but get this error
Copy-Item : Cannot evaluate parameter 'Destination' because its
argument is specified as a script block and there is no input. A
script block cannot be evaluated without input.
and I can't figure out how to write this without writing destination as a Script block
Any help would be very appreciated
Try to pass the destination argument like this:
-Destination $(. { script_block_body_here })
() . { } is Dot sourcing operator and $( ) is Subexpression operator.
($RougePictures = Get-Childitem -Path "C:\" -recurse -file -Include "*.JPG")|
Foreach-object {
copy-Item $_.DirectoryName -Destination "C:\Folders\PictureLibrary"
}
$MainFolders = get-childitem -Path "D:\(1) PROJECTS & PORTFOLIOS\PictureLibrary"
$a=0
For ($a; $a -lt $MainFolders.count; $a++) {
Foreach ($Rouge in $RougePictures){
if ((compare-object (split-path $Rouge.directoryname -leaf) $MainFolders[$a] -IncludeEqual).sideindicator -eq '=='){
move-item $Rouge.fullname -destination $MainFolders[$a].fullname
}
}
}
Found a work around for using a Script block as the destination argument.
Used
if ((compare-object (split-path $Rouge.directoryname -leaf) $MainFolders[$a] -IncludeEqual).sideindicator -eq '==')
after I copied the folders copy-Item $_.DirectoryName -Destination "C:\Folders\PictureLibrary" to find those that matched the folders the pictures are in. If it finds a match '==' the move-item moves those files.
Hope this helps.

Move folders and contents with Powershell

OK, trying to copy folders and contents from a UNC path (shared drive) to another UNC path (NAS) based on date (Before 01 Jan 2015). Yes I know the code says 2017 but once I get it working on test then I'll change the date and run on prod.
#Original file path
$path = "UNC Path"
#Destination file path
$destination = "Different UNC Path"
#It makes a filelist of what's inside the $path path
Foreach($file in (Get-ChildItem $path)) {
#If the lastwrite time is before the given date
If($file.LastWriteTime -lt "01/01/2017") {
#It copies the file to the destination
Copy-Item -Path $file.fullname -Destination $destination -Force } }
It copies the contents of folders fine but not the folders. I think I'm missing a -recurse but putting it after Get-ChildItem $path didn't work.
I plan to get this working then add a Remove-Item line to remove all the old items from the file server.
Thoughts? Suggestions of better ways to accomplish this?
Thanks,
I think you're just missing the -Recurse from Get-ChildItem, but I would do it like so:
Get-ChildItem -Path $Path -Recurse `
| Where-Object { $_.LastWriteTime -lt '2017-01-01' } `
| ForEach-Object {
Copy-Item -Path $_.FullName -Destination ($_.FullName.Replace($source,$destination)) -Force;
}
If you've got hidden or system files to copy, you'll also want the -Force parameter on Get-ChildItem.
Actually, you might need to do this:
Get-ChildItem -Path $Path -Recurse `
| Where-Object { $_.LastWriteTime -lt '2017-01-01' } `
| ForEach-Object {
if ($_.PSIsContainer -and !(Test-Path($_.FullName.Replace($source,$destination)) {
mkdir ($_.FullName.Replace($source,$destination));
}
else {
Copy-Item -Path $_.FullName -Destination ($_.FullName.Replace($source,$destination)) -Force;
}
}

Copy-Item and exclude folders

I need to copy all of my c:\inetpub directory to a new location but exclude the following folders and their subfolders:
c:\inetpub\custerr
c:\inetpub\history
c:\inetpub\logs
c:\inetpub\temp
c:\inetpub\wwwroot
So far I am doing this:
# Directory name is created with a format string
$dirName = "\\servername\folder1 _ {0}\inetpub" -f (get-date).ToString("yyyy-MM-dd-hh-mm-ss")
$dirName # Check the output
# Create dir if needed
if(-not (test-path $dirName)) {
md $dirName | out-null
} else {
write-host "$dirName already exists!"
}
#Copy Backup File to Dir
Copy-Item "\\servername\c$\inetpub\*" $dirName -recurse
This is a simple example of something you could do. Build an array of the parent folders that you want to exclude. Since you are accessing them via UNC paths we cannot really use the c:\ path (We can get around this but what I am about to show should be good enough.).
Then use Get-ChildItem to get all the folders in the inetpub directory. Filter out the exclusions using -notin and pass the rest to Copy-Item
$excludes = "custerr","history","logs","temp","wwwroot"
Get-ChildItem "c:\temp\test" -Directory |
Where-Object{$_.Name -notin $excludes} |
Copy-Item -Destination $dirName -Recurse -Force
You need at least PowerShell 3.0 for this to work.
Copy-Item -Path (Get-Item -Path "$path\*" -Exclude ('Folder1', 'File.cmd', 'File.exe', 'Folder2')).FullName -Destination $destination -Recurse -Force
Replace:
$path by your source folder
('Folder1', 'File.cmd', 'File.exe', 'Folder2') by your specific files/folder to exclude
$destination by your destination folder
Oh, the answer was SO simple, but it seems we are all PowerShell noobs.
New-Item -ItemType Directory -Force -Path $outDir # directory must exist
Copy-Item $inDir\* $outDir -Exclude #("node_modules",".yarn") -Recurse
It's the \* that makes it work.
PowerShell is awesome, but...
I wrote this for daily use and packaged it in the script module, it maintains all the directory structure and supports wildcards:
function Copy-Folder {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[String]$FromPath,
[Parameter(Mandatory)]
[String]$ToPath,
[string[]] $Exclude
)
if (Test-Path $FromPath -PathType Container) {
New-Item $ToPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
Get-ChildItem $FromPath -Force | ForEach-Object {
# avoid the nested pipeline variable
$item = $_
$target_path = Join-Path $ToPath $item.Name
if (($Exclude | ForEach-Object { $item.Name -like $_ }) -notcontains $true) {
if (Test-Path $target_path) { Remove-Item $target_path -Recurse -Force }
Copy-Item $item.FullName $target_path
Copy-Folder -FromPath $item.FullName $target_path $Exclude
}
}
}
}
Just call the Copy-Folder -FromPath inetpub -ToPath new-inetpub -Exclude custerr,history,logs,temp,wwwroot
The -FromPath and -ToPath can be omitted,
Copy-Folder inetpub new-inetpub -Exclude custerr,history,logs,temp,wwwroot
You can do something along the lines of:
?{$_.fullname -notmatch '\\old\\'}
after you get a hold of all your folders to filter them down.
This example would exclude anything containing "old" in the name. You can do this for directory you wish to exclude.
A full example:
C:\Example*" -include "*.txt -Recurse |
?{$_.fullname -notmatch '\\old\\'}|
% {Copy-Item $_.fullname "C:\Destination\"}
For multiple excludes you can use -And :
C:\Example*" -include "*.txt -Recurse |
?{$_.fullname -notmatch '\\old\\' -And $_.fullname -notmatch '\\old2\\'}|
% {Copy-Item $_.fullname "C:\Destination\"}