If I am using PowerShell to search for folders in a drive to move them to another folder from a CSV. I'm drawing a blank on how to leave a text file for the folder that is moved in a replacement folder.
Current PowerShell code to locate folder & move:
$File = Import-Csv C:\share\test\files.txt
foreach ($fileName in $File.FileName) {
Move-Item -Path "C:\share\test\OldLocation\$fileName" -Destination "C:\share\test\NewLocation\$fileName"
}
If I go by the title of this question and suppose you want to move files to a new location,
AND your CSV looks anything like this:
FileName
file1.docx
file2.docx
file3.docx
image1.jpg
This should do it:
$oldLocation = 'C:\share\test\OldLocation'
$newLocation = 'C:\share\test\NewLocation'
# this is the path and filename for the text to leave behind
$movedFiles = Join-Path -Path $oldLocation -ChildPath 'Files Moved.txt'
$messages = #()
$filesToMove = Import-Csv 'C:\share\test\files.txt'
foreach ($file in $filesToMove.FileName) {
$oldFile = Join-Path -Path $oldLocation -ChildPath $file
$newFile = Join-Path -Path $newLocation -ChildPath $file
if (Test-Path -Path $oldFile -PathType Leaf) {
################################################################################################
# WARNING: Using parameter '-Force' will overwrite any file in the new location with that name.
# If that is not what you want, what will be your strategy ?
################################################################################################
Move-Item -Path $oldFile -Destination $newFile # -Force
# add a new line for the text file
$messages += "File '$file' has been moved to '$newLocation'"
}
}
if ($messages.Count) {
# write the textfile with all the files that have been moved in the old location
Add-Content -Path $movedFiles -Value ($messages -join [Environment]::NewLine)
}
else {
Write-Warning "No files have been moved."
}
After the files have been moved, the old location should have a textfile containing
File 'file1.docx' has been moved to 'C:\share\test\NewLocation'
File 'file2.docx' has been moved to 'C:\share\test\NewLocation'
File 'file3.docx' has been moved to 'C:\share\test\NewLocation'
File 'image1.jpg' has been moved to 'C:\share\test\NewLocation'
Related
The code below has been wonderful so far for organising my hard-drives.
I do face this error when I transfer large amounts of data:
Move-Item : Cannot create a file when that file already exists.
This happens when I move a file that is duplicate, is there a way to rename the duplicate file in some sort of sequence?
That would be much appreciated :))
# Get all files
Get-ChildItem "C:\zAa" -File -Recurse | ForEach-Object {
# Get the modified date
$dt = Get-Date $_.LastWriteTime
$year = $dt.Year
$month = $dt.Month
# This adds "0" in front of the 1-9 months
if($dt.Month -lt 10) {
$month = "0" + $dt.Month.ToString()
} else {
$month = $dt.Month
}
# Remove leading '.' from the extension
$extension = $_.Extension.Replace(".", "")
# Where we want to move the file
$destinationFolder = "C:\zBb\$extension\$year\$month\"
# Ensure full folder path exists
if(!(Test-Path $destinationFolder)) {
New-Item -ItemType Directory -Force -Path $destinationFolder
}
# Copy/Move the item to it's new home
Move-Item $_.FullName $destinationFolder
}
I haven't been able to do much, I normally go find the duplicates and rename them manually.
Probably the easiest way to move a file with a unique name is to use a Hashtable that stores the filenames already present.
Then a simple loop can add a sequence number to its file name until it is no longer found in the Hashtable.
Next simply move the file under that new name.
Your code modified:
# Where we want to move the file
$destinationFolder = 'C:\zBb\{0}\{1:yyyy}\{1:MM}' -f $_.Extension.TrimStart("."), $_.LastWriteTime
# Ensure full folder path exists
$null = New-Item -Path $destinationFolder -ItemType Directory -Force
# create a Hashtable and store the filenames already present in the destination folder
$existing = #{}
Get-ChildItem -Path $destinationFolder -File | ForEach-Object { $existing[$_.Name] = $true }
# Get all source files
Get-ChildItem "C:\zAa" -File -Recurse | ForEach-Object {
# Copy/Move the item to it's new home
# construct the new filename by appending an index number in between brackets
$newName = $_.Name
$count = 1
while ($existing.ContainsKey($newName)) {
$newName = "{0}({1}){2}" -f $_.BaseName, $count++, $_.Extension
}
# add this new name to the Hashtable so it exists in the next run
$existing[$newName] = $true
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $destinationFolder -ChildPath $newName
Write-Verbose "Moving '$($_.FullName)' as '$newFile'"
$_ | Move-Item -Destination $newFile -Force
}
I am trying to create myself a powershell script that has mainly two tasks - one after the other.
Initial assumptions: it runs where it performs the following tasks.
Trace all the files, those that are folders - make a zip of them, then after making the archive, delete them. Objective accomplished.
Write to the file the names of all files in the folder (after completing point 1) but check if the given file has the extension of *.zip
a) if it has extension of *.zip then it should be saved in .txt file like "uresoruce = foo/file.zip"
b) if it doesn't have extension *.zip then it should be saved to a .txt file like this "resoruce = foo/file2.jar"
c) since the script is started from the place where all the files are, it will probably also be saved to a file, I would like to avoid this
Suppose we have some files in a folder after compiling, and we don't have any folder inside. The .txt file should look like the following:
uresource = plugins/Liula_1.0.0.0.zip
uresource = plugins/Liborts_3.7.1.0.zip
uresource = plugins/Liwer_1.2.0.0.zip
resource = plugins/o0.I20100512-1500.jar
resource = plugins/or.v20100505-1235.jar
The script I managed to write so far:
## set current path
$path = (Resolve-Path .\).Path
## dirs in a path
$source = Get-ChildItem -Path $path -Filter "" -Directory
$files = Get-ChildItem $path
Add-Type -assembly "system.io.compression.filesystem"
Foreach ($s in $source) {
$destination = Join-path -path $path -ChildPath "$($s.name).zip"
[io.compression.zipfile]::CreateFromDirectory($s.fullname, $destination)
Remove-Item $s -Recurse
}
# This does not working!!! :/
Foreach ($f in $files) {
$extn = [IO.Path]::GetExtension($f)
if ($extn -eq ".zip" ) {
$outfile = "uresource = plugins/" + $f.Name
}
else {
$outfile = "resource = plugins/" + $f.Name
}
}
As #Santiago Squarzon rightly pointed out I did not do anything with this variable. I got a little confused because before I did it only for out-file I didn't use -append and in fact I got the last value in .txt. Now I made my first script in ph, it works like a dream ;)
## set current path
$path = (Resolve-Path .\).Path
## dirs in a path
$source = Get-ChildItem -Path $path -Filter "" -Directory
$files = Get-ChildItem $path
Add-Type -assembly "system.io.compression.filesystem"
## create zip and delete other dirs
Foreach ($s in $source) {
$destination = Join-path -path $path -ChildPath "$($s.name).zip"
[io.compression.zipfile]::CreateFromDirectory($s.fullname, $destination)
Remove-Item $s -Recurse
}
## output filename in .txt
$logFile = "$pwd\logfile.txt"
Foreach ($f in $files) {
$extn = [IO.Path]::GetExtension($f)
if ($extn -eq ".zip" ) {$outfile = "uresource = plugins/$f"}
else {$outfile = "resource = plugins/$f"}
$outfile | Out-File -Append $logFile
}
I have a folder that contains 2000+ Jpeg images
I want to copy some images from this folder to another folder based on the filename that I have in sort.txt file.
I have a filename in text format. how this can be done?
Source is E:\SORT\RUSH
Destination is E:\SORT\SORTED
filename list is E:\sort.txt
$file_list = Get-Content E:\sort.txt
foreach ($file in $file_list) {
$subf = $file.Split('-')[0]
$source = "E:\SORT\RUSH"
Copy-Item $source E:\SORT\SORTED -WhatIf
}
Ok, so now we know the input textfile only contains file BaseNames (no extension), you need to check if such a file can be found in the $source directory.
Try
$source = 'E:\SORT\RUSH'
$destination = 'E:\SORT\SORTED'
# first, make sure the destination folder exists
$null = New-Item -Path $destination -ItemType Directory -Force
# make sure no duplicates are in the text file
$file_list = Get-Content -Path 'E:\sort.tx' | Select-Object -Unique
foreach ($file in $file_list) {
# create the full path and filename for the file to copy
$sourceFile = Join-Path -Path $source -ChildPath "$file.jpg"
# if the file can be found, copy it
if (Test-Path -Path $sourceFile -PathType Leaf) {
Copy-Item -Path $sourceFile -Destination $destination
}
else {
Write-Warning "Could not find file '$sourceFile'"
}
}
I've seen similar questions and used them as a basis for what i'm trying to do here. I have a folder with many files that are named like "Author Name - Book Title.azw".
I'd like to create sub-folders for each author and move all their books into that folder. This is the script i have so far. It successfully creates folder for the Authors, but it chokes on the move-item, says Could not find a part of the path.
$files = Get-ChildItem -file
foreach ($file in $files){
$title = $file.ToString().Split('-')
$author = $title[0]
if (!(Test-Path $author))
{
Write-Output "Creating Folder $author"
New-Item -ItemType Directory -Force -Path $author
}
Write-Output "Moving $file to $author"
Move-Item -Path $file -Destination $author -Force
}
You have to use this:
Get-ChildItem -file | foreach {
$title = $_.ToString().Split('-')
$author = $title[0]
if (!(Test-Path $author))
{
Write-Host "Creating Folder $($author)"
New-Item -ItemType Directory -Force -Path "$author"
}
Write-Host "Moving $($_) to $($author)"
Move-Item -Path "$_" -Destination "$author" -Force
}
You must surround file paths in double quotes. So your code was not working.
From my understanding, you want to move files in a directory to sub folders where the author names are used as the sub folder names. You can try this solution to achieve this.
# Folder which contains files
$Path = "PATH_TO_FILES"
# Make sure path is a directory
if (Test-Path $Path -PathType Container) {
# Go through each file in folder
Get-ChildItem -Path $Path | ForEach-Object {
# Get full path of file
$filePath = $_.FullName
# Extract author
$author = $_.BaseName.Split("-")[0].Trim()
# Create subfolder if it doesn't exist
$subFolderPath = Join-Path -Path $Path -ChildPath $author
if (-not (Test-Path -Path $subFolderPath -PathType Container)) {
New-Item -Path $subFolderPath -ItemType Directory
}
# Move file to subfolder
Move-Item -Path $filePath -Destination $subFolderPath
}
}
I've finally have given up googling and come here out of desperation. Go easy on me I'm fairly new to Powershell.
So, the objective of the code below was to first look through the source folder, then read through each .zip file and move to the directory specified by the value in the hashtable. Unfortunately, this is not how they want it to work anymore.
Now I need to retain the parent folder from source: for example "DAL" and then create the proceeding folders based on the file names and finally move each .zip to its file specified folder. Also, it needs to go through each folder under source which will be at least 20 other folders with a unique 3 character names.
$srcRoot = "C:\Cloud\source\dal"
$dstRoot = "C:\Cloud\Destination"
##$map = #{}; dir -recurse | ? { !$_.psiscontainer} | % { ##$map.add($_.name,$_.PSChildName) }
# DAT and DEV will have to be excluded from folder creation
$map = {
#AEODDAT_201901 = "AEOD\2019\01"
#AEOMDEV_201902 = "AEOM\2019\01"
#AEOYDAT_201902 = "AEOY\2019\01"
}
$fileList = Get-ChildItem -Path $srcRoot -Filter "*.zip*" -File -Force -Recurse
foreach ($file in $fileList)
{
#Go through each file up to mapped string
$key = $file.BaseName.Substring(0,14)
if ($key -in $map.Keys)
{
$fileName = $file.Name
$dstDir = Join-Path -Path $dstRoot -ChildPath $map[$key]
#create direcotory if not in path
if (-not (Test-Path -Path $dstDir))
{
mkdir -Path $dstDir
}
Write-Verbose "Moving $($file.FullName)"
if (Test-Path -Path (Join-Path -Path $dstDir -ChildPath $fileName))
{
#Write error if name exists
Write-Error -Message "File $fileName already exists at $dstDir"
#move path
} else {
Move-Item -Path $($file.FullName) -Destination $dstDir
}
}
}
So C:\Cloud\source\DAL\AEODDAT20190101.zip should create folders in C:\Cloud\Destination\DAL\AEOD\2019\01\AEODDAT20190101.zip would be my desired output.
Welcome, Matt! (no pun intended) One of the habits I have in similar situations with destination folders is to Set-Location $dstRoot and create folders from the relative path. You can execute New-Item with the relative path and the syntax is simpler. For example, your If statement could look like this and it would work the same way (with a slightly different error message):
if ($key -in $map.Keys){
Set-Location $dstRoot
New-Item -ItemType Directory $map[$key] -ErrorAction Ignore #won't raise an error if it exists
Write-Verbose "Moving $($file.FullName)"
#this will raise an error if the file already exists, unless you specify -Force
Move-Item "$($file.FullName)" $map[$key]
}
EDIT: Found 2 issues.
$map is a Hashtable literal that should be preceded with #:
$map = #{
AEODDAT20190101 = "AEOD\2019\01"
You were missing the last character of the base file name by taking only the first 14 characters. AEODDAT2019010 didn't match AEODDAT20190101. This should fix it:
$key = $file.BaseName.Substring(0,15)