Create Folders and Moving Files in Powershell - powershell

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
}
}

Related

Powershell script to copy folders/files from text file

I am trying to copy all folders (and all files) from one folder to another in Powershell where the folders are listed in a text file. I have a script that successfully copied the folders, but the files did not copy over.
$file_list = Get-Content C:\Users\Desktop\temp\List.txt
$search_folder = "F:\Lists\Form601\Attachments\"
$destination_folder = "C:\Users\Desktop\601 Attachments 2021b"
foreach ($file in $file_list) {
$file_to_move = Get-ChildItem -Path $search_folder -Filter $file -Recurse -ErrorAction SilentlyContinue -Force | % { $_.FullName}
if ($file_to_move) {
Copy-Item $file_to_move $destination_folder
}
}
List.text contains folders like:
4017
4077
4125
I would use Test-Path on each of the folders in the list to find out if this folder exists. If so do the copy.
$folder_list = Get-Content -Path 'C:\Users\Desktop\temp\List.txt'
$search_folder = 'F:\Lists\Form601\Attachments'
$destination_folder = 'C:\Users\Desktop\601 Attachments 2021b'
# first make sure the destination folder exists
$null = New-Item -Path $destination_folder -ItemType Directory -Force
foreach ($folder in $folder_list) {
$sourceFolder = Join-Path -Path $search_folder -ChildPath $folder
if (Test-Path -Path $sourceFolder -PathType Container) {
# copy the folder including all files and subfolders to the destination
Write-Host "Copying folder '$sourceFolder'..."
Copy-Item -Path $sourceFolder -Destination $destination_folder -Recurse
}
else {
Write-Warning "Folder '$folder' not found.."
}
}

Powershell script to create a single folder based of the 5 digits in the name of the pdf's

So far I have tried the following script:
$SourceFolder = "D:\WORK\JetLetter\LKY\LKY_jV_004\"
$TargetFolder = "D:\WORK\JetLetter\LKY\LKY_jV_004\Final\"
Get-ChildItem -Path $SourceFolder -Filter *.pdf |
ForEach-Object {
$ChildPath = Join-Path -Path $_.Name.Replace('.pdf','') -ChildPath $_.Name
[System.IO.FileInfo]$Destination = Join-Path -Path $TargetFolder -ChildPath $ChildPath
if( -not ( Test-Path -Path $Destination.Directory.FullName )){
New-Item -ItemType Directory -Path $Destination.Directory.FullName
}
Copy-Item -Path $_.FullName -Destination $Destination.FullName
}
This creates a folder for every pdf in the folder.
I need it create a single folder based on the 5 digit in the name and move those files into the new folder.
For example: I could have 10 pdf's that have the number "30565" in them and the new folder should be named "30565"
Here are some file names to explain:
LKY_20974_Pr01_1-5000.pdf
to
D:\WORK\JetLetter\LKY\LKY_jV_004\Final\20974
LKY_20974_Pr02_5001-10000.pdf
to
D:\WORK\JetLetter\LKY\LKY_jV_004\Final\20974
LKY_20974_Pr03_10001-15000.pdf
to
D:\WORK\JetLetter\LKY\LKY_jV_004\Final\20974
I have tried to include an else block to the best answer script and haven't had much success. I did however create a separate script that will archive the files before creating a new file. I just have to run it before the main powershell script.
$SourceDir = 'D:\WORK\JetLetter\LKY\LKY_jV_004_9835'
$DestDir = 'D:\WORK\JetLetter\LKY\#Print_Production_Files'
$ArchiveDir = 'D:\WORK\JetLetter\LKY\#Print_Production_Files\#archive'
$Filter = '*.pdf'
$FileList = Get-ChildItem -LiteralPath $SourceDir -Filter $Filter -File
foreach ($FL_Item in $FileList)
{
# this presumes the target dir number is ALWAYS the 2nd item in the split string
$TargetDir = $FL_Item.BaseName.Split('_')[1]
$FullTargetDir = Join-Path -Path $DestDir -ChildPath $TargetDir
if (Test-Path -LiteralPath $FullTargetDir)
{
# the "$Null =" is to suppress unwanted output about what was done
$null = Move-Item -Path $FullTargetDir -Destination $ArchiveDir -Force
}
}
This has made the files and folders a lot more organized.
i think this does what you want done. [grin] the comments seem adequate, but if you have any questions, please ask.
$SourceDir = 'c:\temp\JetLetter\LKY\LKY_jv_004'
$DestDir = 'c:\temp\JetLetter\LKY\LKY_jv_004\Final'
$Filter = '*.pdf'
#region >>> make the dirs and sample files to work with
# remove the entire "#region/#endregion" block when you are ready to work with real data
# make the dirs
$Null = mkdir -Path $SourceDir, $DestDir -ErrorAction 'SilentlyContinue'
# make the test files
$SampleFiles = #(
'LKY_11111_Pr11_1-11111.pdf'
'LKY_22222_Pr22_2-22222.pdf'
'LKY_22222_Pr22_2222-2222.pdf'
'LKY_33333_Pr33_3-3333.pdf'
'LKY_33333_Pr33_33333-33333.pdf'
'LKY_55555_Pr55_5-5555.pdf'
'LKY_77777_Pr77_7-77777.pdf'
'LKY_77777_Pr77_77777-77777.pdf'
'LKY_99999_Pr99_9-99999.pdf'
)
foreach ($SF_Item in $SampleFiles)
{
# the "$Null =" is to suppress unwanted output about what was done
$Null = New-Item -Path $SourceDir -Name $SF_Item -ItemType 'File' -ErrorAction 'SilentlyContinue'
}
#endregion >>> make the dirs and sample files to work with
$FileList = Get-ChildItem -LiteralPath $SourceDir -Filter $Filter -File
foreach ($FL_Item in $FileList)
{
# this presumes the target dir number is ALWAYS the 2nd item in the split string
$TargetDir = $FL_Item.BaseName.Split('_')[1]
$FullTargetDir = Join-Path -Path $DestDir -ChildPath $TargetDir
if (-not (Test-Path -LiteralPath $FullTargetDir))
{
# the "$Null =" is to suppress unwanted output about what was done
$Null = New-Item -Path $FullTargetDir -ItemType 'Directory'
}
$NewFullFileName = Join-Path -Path $FullTargetDir -ChildPath $FL_Item.Name
# leave the file in the source dir if it already is in the final target dir
# you may want to save the not-copied info to a file for later review
if (-not (Test-Path -LiteralPath $NewFullFileName))
{
# the "Move-Item" cmdlet on win7ps5.1 is wildly unreliable
# so i used copy & then remove
$Null = Copy-Item -LiteralPath $FL_Item.FullName -Destination $NewFullFileName
Remove-Item -LiteralPath $FL_Item.FullName
}
else
{
Write-Warning (' {0} already exists in {1}' -f $FL_Item.Name, $FullTargetDir)
Write-Warning ' The file was not moved.'
Write-Warning ''
}
}
screen output only exists for "not moved" files. again, you may want to save the list to a $Var or to a file for later work.
one of the moved files ...
C:\Temp\JetLetter\LKY\LKY_jv_004\Final\22222\LKY_22222_Pr22_2222-2222.pdf

Compress-Archive handle files with same name in Powershell

I'm writing an archiving script which collecting desired files to an array then adding them to an archive 1 by 1.
I came to a problem when there is DIR1/file.ext and DIR2/file.ext because DIR2's file going to overwrite the previous.
How can I set unique filename or how it's possible to solve it on the fly instead of copying files to a dir with structures then zip the whole dir?
Here is my code:
# GET FILE LIST
$outgoingfiles = Get-ChildItem -Depth 1 -Filter "*.EXT" | Where-Object { $_.DirectoryName -like "*OUTGOING*" }
# Handle if OUTGOING/archive dir is exists
if(-not (Test-Path "OUTGOING/archive")) {
New-Item -Path "OUTGOING/archive" -ItemType Directory
}
# ZIP outgoing files
ForEach ($outgoing in $outgoingfiles) {
Compress-Archive $outgoing.FullName -Update -DestinationPath $zippath
}
Thank you!
I don't think there is a way to tell Compress-Archive to rename files when a file with the same name is already included in the zip.
What you can do is create a temporary folder, copy all files to there and if needed rename them. Then create the zip file using the unique files in that folder.
Finally, remove the temp folder again:
$zippath = 'D:\Test\OutGoing.zip' # path and filename for the output zip file
$rootPath = 'D:\Test' # where the files can be found
# create a temporary folder to uniquely copy the files to
$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([Guid]::NewGuid().Guid)
$null = New-Item -ItemType Directory -Path $tempFolder
# create a hashtable to store the fileHash already copied
$fileHash = #{}
# get the list of files and copy them to a temporary folder
Get-ChildItem -Path $rootPath -Depth 1 -Filter '*.EXT' -File | Where-Object { $_.DirectoryName -like "*OUTGOING*" } | ForEach-Object {
$count = 1
$newName = $_.Name
# test if the file name is already in the hash and if so, append a counter to the basename
while ($fileHash.ContainsKey($newName)) {
$newName = "{0}({1}){2}" -f $_.BaseName, $count++, $_.Extension
}
# store this file name in the hash and copy the file
$fileHash[$newName] = $true
$newFile = Join-Path -Path $tempFolder -ChildPath $newName
$_ | Copy-Item -Destination $newFile -Force
}
# append '*.*' to the temporary folder name.
$path = Join-Path -Path $tempFolder -ChildPath '*.*'
# next, get the list of files in this temp folder and start archiving
Compress-Archive -Path $path -DestinationPath $zippath -Update
# when done, remove the tempfolder and files
Remove-Item -Path $tempFolder -Force -Recurse
Hope that helps
I would just copy the files along with their parent directories to a destination folder, then zip it up with Compress-Archive. Then you don't have to worry about making filenames unique.
Demo:
$sourceFolder = "C:\\"
$destinationFolder = "C:\\OUTGOING"
# Create destination folder if it doesn't exist
if (-not(Test-Path -Path $destinationFolder -PathType Container))
{
New-Item -Path $destinationFolder -ItemType Directory
}
# Get all .exe files one level deep
$files = Get-ChildItem -Path $sourceFolder -Depth 1 -Filter *.ext
foreach ($file in $files)
{
# Get standalone parent directory e.g. DIR1, DIR2
$parentFolder = Split-Path -Path (Split-Path -Path $file.FullName) -Leaf
# Create destination path with this parent directory
$destination = Join-Path -Path $destinationFolder -ChildPath $parentFolder
# Create destination parent directory if it doesn't exist
if (-not(Test-Path -Path $destination -PathType Container))
{
New-Item -Path $destination -ItemType Directory
}
# Copy file to parent directory in destination
Copy-Item -Path $file.FullName -Destination $destination
}
# Zip up destination folder
# Make sure to pass -Update for redoing compression
Compress-Archive -Path $destinationFolder -DestinationPath "OUTGOING.zip" -Update -CompressionLevel Optimal

How to do custom file copy in Powershell?

Below power shell script will copy and replace all the contents from source to destination.
Copy-Item -Path $sourcePath -Destination $installationPath -Recurse -Force
This looks simple. But in my case, I need to implement a custom file copy logic.
Logging everything about files being copied.
Skipping certain set of folders.
Sample script:
[Array]$files=Get-ChildItem ($sourceDirectoryPath) -Recurse | Format-Table Name, Directory, FullName
for($i=0;$i -le $files.Length-1;$i++)
{
. . . . . .
# Build destination path based on the source path.
# Check and create folders if it doesn't exists
# Add if cases to skip certain parts.
Copy-Item -Force $sourcePath -Destination $destinationPath
}
Any ideas on how to achieve this? Any other ideas also will help.
Thanks.
Something like this:
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$sourceDir = "c:\temp\source"
$targetDir = "c:\temp\target"
$skipFiles = #(
"skip.me",
"and.me"
)
Get-ChildItem -Path $sourceDir -Recurse | ForEach-Object {
# ignore folders
if ($_.PSIsContainer)
{
return
}
# skip this file?
if ($skipFiles -contains $_.Name)
{
Write-Verbose "Skipping '$_.FullName'"
return
}
# create target folder when needed
$path = $_.DirectoryName -replace "^$([RegEx]::Escape($sourceDir))",$targetDir
if (!(Test-Path $path))
{
Write-Verbose "Creating target path '$path'..."
New-Item -Path $path -ItemType Directory
}
# copy the file
Write-Verbose "Copying '$_.FullName' to '$path'..."
Copy-Item $_.FullName $path | Out-Null
}

Merge two directories keeping any files with same name

I am looking for some help to create a PowerShell script to merge or copy one directory to another that the destination directory has files with the same name as the source.
I need to keep both, the script can append a number to the source file if it has a file of duplicate name in the destination.
Here is a sample script that deals with one file, but I need to set a directory and let it loose recursively on the entire directory.
$SourceFile = "C:\Temp\File.txt"
$DestinationFile = "C:\Temp\NonexistentDirectory\File.txt"
if ((Test-Path $DestinationFile) -eq $false) {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile
try this
$SourceDir = "C:\Temp"
$DestinationDir = "C:\Temp2\NonexistentDirectory"
#create dir if not exists (dont remove if exist)
New-Item -ItemType Directory -Path $DestinationDir -Force
#get list files destination dir
$DestinationFiles=gci $DestinationDir -File
#loop on file source and create newname for copy while name exist already
gci $SourceDir -File | %{
$counter=0
$name=$_.Name
while ($name -in $DestinationFiles.Name)
{
$counter++;
$name="{0}_{1:d6}{2}" -f $_.BaseName, $counter, $_.Extension
}
Copy-Item -Path $_.FullName -Destination "$DestinationDir\$name"
}