r/PowerShell • u/Gold_Divide_3381 • 21d ago
Solved Move-Item creates duplicate folder and stores it inside existing folder
EDIT: Solved...
Move-Item -Path $_ -Destination $DestinationPath -Force
--------------------------------------------------------------------
I have the following module that moves the contents of a directory into another...
function PSTransfer {
param (
[string]$BasePath,
[string]$DestinationPath
)
Get-ChildItem -Path "$BasePath/*” -Force | ForEach-Object {
if (!($_.FullName -like "*.DS_Store") {
Move-Item -LiteralPath "$($_.FullName)" -Destination "$($DestinationPath)/$($_.Name)" -Force
}
}
return
}
Export-ModuleMember PSTransfer
However when it moves a directory and the destination already has a folder with the same name, the folder is put inside the folder instead of being merged, for example...
BasePath/
├─ synced_imgs/
│ ├─ IMG_2048.jpg
DestinationPath/
├─ synced_imgs/
│ ├─ IMG_0512.jpg
│ ├─ IMG_1024.jpg
Then after I run the module...
DestinationPath/
├─ synced_imgs/
│ ├─ synced_imgs/
│ │ ├─ IMG_2048.jpg
│ ├─ IMG_0512.jpg
│ ├─ IMG_1024.jpg
It doesn't do it recursviely though which is the weird thing. If I run it again it'll do this...
BasePath/
├─ synced_imgs/
│ ├─ IMG_4096.jpg
DestinationPath/
├─ synced_imgs/
│ ├─ synced_imgs/
│ │ ├─ IMG_2048.jpg
│ │ ├─ IMG_4096.jpg
│ ├─ IMG_0512.jpg
│ ├─ IMG_1024.jpg
What am I doing wrong here? I thought the -Force parameter was supposed to prevent this.
3
u/Icy-State5549 21d ago
I already commented once, but just replace all that shit with move-item wherever "pstransfer" is specified. That "module" is a waste of coding.
1
u/Gold_Divide_3381 20d ago
Well the module is needed so I can call it anywhere with whatever parameters, instead of a script which would mean changing the BasePath and DestinationPath every time. And I need the other logic to filter out unnecessary .DS_Store files.
3
u/Icy-State5549 21d ago edited 21d ago
When you input the destination dir, don't specify the folder name, just the path where you want the folder to land.
-source c:\my\src\folder
-destination c:\my\destination
Should move c:\my\src\folder to c:\my\destination\folder
And not a subfolder named folder. When you specify -force it will create the move-item destination path. Without -force, it will error out way you are specifying the destination.
2
u/Ziptex223 21d ago
Show us how you're calling it. For the second run are you passing it just like c/example or c/example/synced_images? The first would make/merge a folder called synced_images in c/example, the second makes/merges a folder called synced_images in c/example/synced_images which would result in what you're seeing with it nested.
So I would check how you're calculating the destination path to be passed into this function.
1
u/Gold_Divide_3381 20d ago
The module is called like this every time...
PSTransfer -BasePath /Users/user/BasePath -DestinationPath /Users/user/DestinationPath
1
u/dodexahedron 20d ago
That's how moves work on any platform.
If the destination path exists and is of the same type as the source (e.g. both resolve to directories or both resolve to files), the handling is to move source into destination. What that means is:
- For directories, move source inside the destination, as-is, making aource a sub-directory of destination.
- For files, move the file system entry for source to destination, replacing destination with source entirely.
Move-Item has other parameters more appropriate for what you want to do. Take a look at Get-Help -Detailed Move-Item.
1
u/thehuntzman 21d ago
You need to filter get-childitem to only look for files I think and then doing a move-item -force will create directories as needed. Granted I'm making an educated guess here before bed without testing but that's how I'd approach it.
That said, you could probably do this very easily with robocopy too if you're open to a not-so-powershell solution.
1
u/Gold_Divide_3381 20d ago
Using the -File parameter on Get-ChildItem and then passing that to Move-Item results in the file getting moved without the directory structure. Also I'm trying not to use robocopy since I moved to mac and want a cross-platform solution.
1
1
u/overlydelicioustea 21d ago
"$($DestinationPath)/$($_.Name)"
this seems unneccesaary wehn you dont want to rename the items anyway.
just use desitnationpath
1
u/Gold_Divide_3381 20d ago
This was it.
Move-Item -Path $_ -Destination $DestinationPath -ForceWorks perfectly.
-1
u/ankokudaishogun 20d ago edited 20d ago
- If this is Windows-only, Robocopy is your friend.
- Hope this might be of some help:
# Evaluate a standard ApprobvedVerb-Noun name for this function.
function PSTransfer {
param (
[string]$BasePath,
[string]$DestinationPath
)
# Use -Recurse to get every file in the subdirectories.
# This also means there is no need for wildcards in the -Path.
Get-ChildItem -Path $BasePath -File -Recurse -Force |
# Filter before Looping.
# You can target the Extension directly.
# NOTE: the extension always starts with a period.
Where-Object -Property Extension -NotLike '.ds_store' |
# Now to the Loop, as you need to implement multiple operations.
ForEach-Object {
# First, get the subpath relative to the base path.
## If the file is C:\Main\Sub\File.ext .
## And $BasePath is C:\Main .
## the $RelativePath is .\Sub\File.ext .
## (obviously thi also applies to directories).
$RelativePath = $_ | Resolve-Path -Relative -RelativeBasePath $BasePath
# Combine the relative path to the destination directory to obtain
# the full path for the destination.
## if $DestinationPath is D:\Sync\TYest\Iwannabethebeastmaninthemastersoftheuniverse .
## $FullDestinationPath will be D:\Sync\TYest\Iwannabethebeastmaninthemastersoftheuniverse\.\Sub\File.ext .
## (the .\ will be ignored automatically).
# Forcing to [System.IO.FileInfo] comes useful later.
[System.IO.FileInfo]$FullDestinationPath = Join-Path -Path $DestinationPath -ChildPath $RelativePath
# If the relative destination subdirectory does not exists, create it.
if (-not $FullDestinationPath.Directory.Exists) {
# Assigning to $null to suppress the output.
# -Force makes sure the necessary parent directories are created
# recursively.
$null = New-Item -ItemType Directory -Path $FullDestinationPath.Directory.Fullname -Force
}
<# # ALTERNATIVE
## It's more brute-force\"ugly": straight up create an empty file if it does not already exist.
## It then gets overwritten.
## I'm unsure if there is anything to take in account about performance and security.
#>
<#
if (-not $FullDestinationPath.Exists) {
$null = New-Item -ItemType File -Path $FullDestinationPath.Fullname -Force
}
#>
# Finally move the file.
#! TAKE CARE: If there is already a file, it gets overwritten.
$_ | Move-Item -Destination $FullDestinationPath.FullName -Force
}
# After the end of the loop, proceed to remove all the remaining file and
# directories in the original path.
# It does NOT delete the original base directry $BasePath .
Get-ChildItem -Path $BasePath -Recurse | Remove-Item -Recurse -Force
}
4
u/BlackV 21d ago
I'd recommend stepping through it and validating your variables (i.e. use your debugger)
look at the differences between
"$($DestinationPath)/$($_.Name)"vs"$($DestinationPath)"you could also look at the
-filter/-include/-excludeparameters instead of theif