I have batch changed multiple files that all start with a prefix of a folder where I need them in.
The files are located on another location, like a folder on the desktop.
For example:
101AA0001.dat
101AA0002.dat
102AA0001.dat
102AA0002.dat
The destination folder will for example be:
C:\destfolder\101\ or C:\destfolder\102\
Files starting with 101 need to go in the 101 folder and the files starting with 102 go to folder 102.
I can find some scripts that creates the folder based on the filename. But in this situation the folders already exist. I also know for sure the files don't exist, so I don't have to overwrite files or something.
I guess it is easy for the people that know PowerShell very well, but I don't know how to do this. Can someone please help me? This can save me a lot of time.
I have tried to move the files with the following rule:
Move-Item -Path C:\Users\Username\Desktop\test*.dat -Destination C:\Users\Username\Desktop\test2\ -include "*.dat"
But it copies the whole folder except for the files.
You can do that quite easily with code like below:
$sourceFolder = Join-Path -Path $env:USERPROFILE -ChildPath 'Desktop'
$destination = 'C:\destfolder'
Get-ChildItem -Path $sourceFolder -File -Filter '*.dat' | ForEach-Object {
$targetFolder = Join-Path -Path $destination -ChildPath $_.Name.Substring(0, 3)
# if the target folder does not exist yet, create it
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
$_ | Move-Item -Destination $targetFolder -WhatIf
}
The -WhatIf switch shows what would happen in the console without actually performing the move. If you are satisfied with what is output, remove that switch.
This will take all files that end in ".dat" from the $Source folder into a subfolder inside the $DestinationRoot named for the first three characters of the ".dat" file.
$Source = "C:\Users\Username\Desktop"
$DestinationRoot = "C:\Users\Username\Desktop\test2"
$Filelist = Get-ChildItem -Path $Source -Filter "*.dat" -File
foreach ($File in $Filelist){ $DestinationFolder = $File.Name.Substring(0,3)
$FinalPath = "$DestinationRoot\$DestinationFolder"
Move-Item -Path $File.Fullname -Destination $FinalPath -Whatif }
Remove the -Whatif when you're ready to run it for real.
This doesn't handle folder creation and should error out if the file already exists in the target location so it won't accidentally overwrite anything.
Related
Here is a section of code from a larger script. The goal is to recurse through a source directory, then copy all the files it finds into a destination directory, sorted into subdirectories by file extension. It works great the first time I run it. If I run it again, instead of overwriting existing files, it fails with this error on each file that already exists in the destination:
Copy-Item : Cannot overwrite the item with itself
I try, whenever possible, to write scripts that are idempotent but I havn't been able to figure this one out. I would prefer not to add a timestamp to the destination file's name; I'd hate to end up with thirty versions of the exact same file. Is there a way to do this without extra logic to check for a file's existance and delete it if it's already there?
## Parameters for source and destination directories.
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
# Build list of files to sort.
$Files = Get-ChildItem -Path $Source -Recurse | Where-Object { !$_.PSIsContainer }
# Copy the files in the list to destination folder, sorted in subfolders by extension.
foreach ($File in $Files) {
$Extension = $File.Extension.Replace(".","")
$ExtDestDir = "$Destination\$Extension"
# Check to see if the folder exists, if not create it
$Exists = Test-Path $ExtDestDir
if (!$Exists) {
# Create the directory because it doesn't exist
New-Item -Path $ExtDestDir -ItemType "Directory" | Out-Null
}
# Copy the file
Write-Host "Copying $File to $ExtDestDir"
Copy-Item -Path $File.FullName -Destination $ExtDestDir -Force
}
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
You are trying to copy files from a source directory to a sub directory of that source directory. The first time it works because that directory is empty. The second time it doesn't because you are enumerating files of that sub directory too and thus attempt to copy files over themselves.
If you really need to copy the files into a sub directory of the source directory, you have to exclude the destination directory from enumeration like this:
$Files = Get-ChildItem -Path $Source -Directory |
Where-Object { $_.FullName -ne $Destination } |
Get-ChildItem -File -Recurse
Using a second Get-ChildItem call at the beginning, which only enumerates first-level directories, is much faster than filtering the output of the Get-ChildItem -Recurse call, which would needlessly process each file of the destination directory.
After searching for a wile I need to post my question here:
I want to do a simple task:
copy-item -path "C:\Folder Copied" -destination "C:\Folder Copied_New" -recurse
Assuming the dir "Folder Copied_New" doenst exist in "C:", PS will create a folder, and copy the folder (and its content) "C:\Folder Copied" to "Folder Copied_New"
HOWEVER, if you execute the command a second time, the following happens:
Powershell created: "C:\Folder Copied_New\Folder Copied" (content "Test.txt" was also copied to this newly created folder...
The 3rd time you execute the command, it ll say that the folder already exists...
So my question:
After I run the command a 2nd time, PS should throw an error, that "Folder Copied_New". How do I do that?
I tried copying and renaming the new folder, using and NOT using "" in the paths, but nothing worked. I do think of using -Testpath, but I thought I ask the community for a simplier (BestPractice) approache.
Thanks in advance for reading and advising!
Well explained in another question but same issue
Not an answer but to long to write in comments.
Can you run following script and show us the output?
$root = "$($env:TEMP)\test"
$source = "$($root)\Logfiles"
$destination = "$($root)\Drawings\Logs"
# Verify folders don't exist yet
if (Test-Path $source) {Throw}
if (Test-Path $destination) {Throw}
# Set up
New-Item $source, $destination -ItemType Directory -Force | Out-Null # Create folders
New-Item "$($source)\testfile" -ItemType File -Force | Out-Null # Create a testfile
# Show files/folders before copy
Write-Output "Before copy"
Get-ChildItem $root -Recurse | select fullname
# Copy first time
Copy-Item -path $source -destination $destination -Recurse -ErrorAction Continue
Write-Output "After first time copy"
Get-ChildItem $root -Recurse | select fullname
# Copy second time
Copy-Item -path $source -destination $destination -Recurse -ErrorAction Continue
Write-Output "After second time copy"
Get-ChildItem $root -Recurse | select fullname
# Copy third time
Copy-Item -path $source -destination $destination -Recurse -ErrorAction Continue
Write-Output "After third time copy"
Get-ChildItem $root -Recurse | select fullname
# Clean up
Remove-Item $source, $destination -Force -Recurse | Out-Null
On my computer, I don't observe any abnormal behavior. The source folder get's copied to destination after the first time. The second run does not alter anything, neither does the third run.
That behavior is the default:
Let's say you want to copy and rename an item, so you are doing this:
Copy-Item -path source.txt -Destination destination.txt
You expect a file named destination.txt with the content of source.txt.
If you want to copy a file but without renaming it you do this:
Copy-Item -path source.txt -Destination C:\test
You expect the file source.txt to apear in C:\test. If C:\test didn't exist it will be created.
Now let's try to copy a folder while the destination does not exist.
Copy-Item -path C:\test -Destination D:\toast
The destination didn't exist so you created it by copying the source folder to the destination. The content of the destination will be the same as the source. The folder D:\toast is the same as C:\test but it got renamed in the process.
However if you provide a destination path where the source object (folder) is going to be located, it will be located in there.
Copy-Item -path C:\test -Destination D:\toast
D:\toast did exist from our previouse action so a copy of C:\test will be created in there: D:\toast\test
When destination folder exists the right way of copying files is defined here
Copy-Item 'C:\Source\*' 'C:\Destination' -Recurse -Force
If destination folder doesn't exist, some files in the source subfolders are copied straight into destination, without keeping original folder structure.
Is there a way to have a single Copy-Item command to address both cases and persist folder structure? Or is it too much to ask from Powershell?
You may want to use a if statement with test-path
this is the script i used to fix this problem
$ValidPath = Test-Path -Path c:\temp
If ($ValidPath -eq $False){
New-Item -Path "c:\temp" -ItemType directory
Copy-Item -Path "c:\temp" -Destination "c:\temp2" -force
}
Else {
Copy-Item -Path "c:\temp" -Destination "c:\temp2" -force
}
I am trying to copy a folder from the local computer to a remote server. It works but if the destination folder already exists it is creating a duplicate folder inside it.
copy-item -Path C:\test -Destination \\server\F$\testpassed -recurse -Force
To copy only the files from within C:\test to the \\server\F$\testpassed folder you need to use the following command:
Copy-Item -Path C:\test\* -Destination \\server\F$\testpassed -Recurse
\* is a wildcard for anything within the folder, and will cause Copy-Item to copy anything within the folder to the Destination. You could also use *.txt to only copy txt files if you wanted only a specific file type to be copied.
EDIT:
I would test for the presence of $TARGETDIR and then create it if needed. This way you only have a single copy command.
$TargetDir = "\\server\F$\testpassed"
$SourceDir = "C:\test"
if(!(Test-Path -Path $TARGETDIR)) {New-Item -Path $TARGETDIR -ItemType Directory}
Copy-Item -Path "$SourceDir\*" -Destination $TARGETDIR -Recurse
Using source path in below way will solve your issue
Copy-Item -Path C:\test*
Try
$Source = Get-childitem C:\test -Recurse
copy-item -Path $Source.FullName -Destination C:\temp -recurse -Force
Use GC to stop getting the folder as well as the contents.
I am trying to write a PowerShell script that will copy a subset of files from a source folder and place them into a target folder. I've been playing with "copy-item" and "remove-item" for half a day and cannot get the desired or consistent results.
For example, when I run the following cmdlet multiple times, the files end up in different locations?!?!:
copy-item -Path $sourcePath -Destination $destinationPath -Include *.dll -Container -Force -Recurse
I've been trying every combination of options and commands I can think of but can't find the right solution. Since I'm sure that I'm not doing anything atypical, I'm hoping someone can ease my pain and provide me with the proper syntax to use.
The source folder will contain a large number of files with various extensions. For example, all of the following are possible:
.dll
.dll.config
.exe
.exe.config
.lastcodeanalysisissucceeded
.pdb
.Test.dll
.vshost.exe
.xml
and so on
The script needs to only copy .exe, .dll and .exe.config files excluding any .test.dll and .vshost.exe files. I also need the script to create the target folders if they don't already exist.
Any help getting me going is appreciated.
try:
$source = "C:\a\*"
$dest = "C:\b"
dir $source -include *.exe,*.dll,*.exe.config -exclude *.test.dll,*.vshost.exe -Recurse |
% {
$sp = $_.fullName.replace($sourcePath.replace('\*',''), $destPath)
if (!(Test-Path -path (split-path $sp)))
{
New-Item (split-path $sp) -Type Directory
}
copy-item $_.fullname $sp -force
}
As long as the files are in one directory, the following should work fine. It might be a bit more verbose than needed, but it should be a good starting point.
$sourcePath = "c:\sourcePath"
$destPath = "c:\destPath"
$items = Get-ChildItem $sourcePath | Where-Object {($_.FullName -like "*.exe") -or ($_.FullName -like "*.exe.config") -or ($_.FullName -like "*.dll")}
$items | % {
Copy-Item $_.Fullname ($_.FullName.Replace($sourcePath,$destPath))
}