Powershell Extract and Rename as ZIP Filename - powershell

Iv got an application that produces a ZIP file with a HTML document inside of it. However the extracted HTML file is not named the same as the zip file (for some strange reason that I have no control over).
I need to extract the zip file and rename the extracted file the same as the zip file name however I am unsure where this would slot in on my script some help would be appreciated please, script below:
#Files Location
$ZipFilesPath = "C:\Test\"
#Unzip To Same Location
$UnzipPath = "C:\Test\"
$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($UnzipPath)
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
$FileCounter = 1
#Clear Initilisation Vars from Console
clear
foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the Filepath
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host "File: " $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1040)
#Move Along to Next File
$FileCounter++
}
Final Solution (With Added Piece From Nick Cox below)
#Location Of ZIP Files Defined Here
$ZipFilesPath = "C:\Test\";
#Unzip To Temp Folder Defined Here
$TempPath = "C:\Test\Temp"
#Create Our Temp File If It Doesnt Exist (Will Be Deleted Again)
If(!(test-path $TempPath))
{
New-Item -ItemType Directory -Force -Path $TempPath
}
$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($TempPath)
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
$FileCounter = 1
#Clear Initilisation Vars from Console
clear
foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the Filepath
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host "File: " $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1040)
#Move Our Temp Files
$HtmlFiles = Get-ChildItem $TempPath *.html
$HtmlFiles |% {Move-Item $_.Fullname "$UnzipPath/$ZipFileActualName.html"}
#Move Along to Next File
$FileCounter++
}
#Remove Our .ZIP Folders
Get-ChildItem -Path $ZipFilesPath -Include *.zip* -File -Recurse | foreach { $_.Delete()}
#Remove Our Temp Folder
Remove-Item –path $TempPath –recurse

Why not extract to a temp location and copy from there to the destination?
#Files Location
$ZipFilesPath = "C:\Test\"
#Unzip To Same Location
$UnzipPath = "C:\Test\"
$TempPath = "C:\Test\Temp"
$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($TempPath)
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
$FileCounter = 1
#Clear Initialisation Vars from Console
clear
foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the Filepath
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host "File: " $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1040)
$HtmlFiles = Get-ChildItem $TempPath *.html
$HtmlFiles |% {Move-Item $_.Fullname "$UnzipPath/$ZipFileActualName.html"}
#Move Along to Next File
$FileCounter++
}
By the way, if you're using PowerShell >= 5.0 you can unzip natively with Expand-Archive.

foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the Filepath
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host "File: " $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1041)
$HtmlFiles = Get-ChildItem $TempPath *.html
$HtmlFiles |% {Move-Item $_.Fullname "$UnzipPath/$ZipFileActualName.html"}
#Move Along to Next File
$FileCounter++
}

Related

Powershell: Search in path, rename file and copy it

I am trying to build a script that can do the following
When I open the powershell script
it should ask me for a path where the files are located e.g.
C:\SearchPath\DateOfToday
Daily there will be a new folder e.g. 20221214.
Maybe it is possible to specify the path and the script will get today's date by itself or maybe just opening a windows explorer so I can select the SourceFolder.
A file must be copied e.g. Test.xmr and then renamed to Test.$xmr
some more files should be collected like
jpl, eml, html, pdf, cml, xmr, $xmr and xml
the collected files should be moved to the folder
C:\AnotherFolder\
What I have so far is this
$SourceFolder = "\\Path1\Dateoftoday"
$DestFolder = '\\Path2' #Path to Destination Folder
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.xmr"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.jpl"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.eml"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.html"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.pdf"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.cml"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.xml"
ForEach ($F in $FileList) {
Copy-Item $F.FullName (Join-Path $DestFolder ($F.Name))
}
I can specify the path and the filenames and then run it.
Sorry if it's basic, but I'm new at powershell.
Looks like you just need this:
$today = '{0:yyyyMMdd}' -f (Get-Date)
$SourceFolder = "\\Path1\$today"
$DestFolder = '\\Path2' #Path to Destination Folder
$filePattern = '*.xmr', '*.jpl', '*.eml', '*.html', '*.pdf', '*.cml', '*.xml', '*.$xmr'
# unclear about your question 2)..
# do we need to copy a file to the $SourceFolder first ??
# Copy-Item -Path 'X:\Somewhere\Test.xmr' -Destination (Join-Path -Path $SourceFolder -ChildPath 'Test.$xmr')
# copy all chosen files to the destination folder
Get-ChildItem -LiteralPath $SourceFolder -Recurse -File -Include $filePattern | ForEach-Object {
$_ | Copy-Item -Destination $DestFolder
}
As per your comment, hopefully this is what you need:
$today = '{0:yyyyMMdd}' -f (Get-Date)
$SourceFolder = "\\Path1\$today"
$DestFolder = '\\Path2' #Path to Destination Folder
$extensions = 'xmr', 'jpl', 'eml', 'html', 'pdf', 'cml', 'xml'
# ask for the filename
$baseName = Read-Host 'Please enter the file name without extension'
if (![string]::IsNullOrWhiteSpace($baseName)) {
# construct the filenames to copy
$pattern = $extensions | ForEach-Object { '{0}.{1}' -f $baseName.Trim(), $_ }
# copy all chosen files to the destination folder
Get-ChildItem -LiteralPath $SourceFolder -Recurse -File |
Where-Object { $pattern -contains $_.Name } | ForEach-Object {
# rename the extensions by prefixing it with a dollar sign ($)
$targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
$_ | Copy-Item -Destination $targetFile
# if you want ONLY the .xmr file to be renamed with as .$xmr use this instead:
# if ($_.Extension -eq '.xmr') {
# $targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
# $_ | Copy-Item -Destination $targetFile
# }
# else {
# $_ | Copy-Item -Destination $DestFolder
# }
}
}
Edit
As you commentd you want to remove the leftover .xmr and .att files after copying, here the code that does that too
$today = '{0:yyyyMMdd}' -f (Get-Date)
$SourceFolder = "\\Path1\$today"
$DestFolder = '\\Path2' #Path to Destination Folder
$extensions = 'xmr', 'jpl', 'eml', 'html', 'pdf', 'cml', 'xml', 'att'
# ask for the filename
$baseName = Read-Host 'Please enter the file name without extension'
if (![string]::IsNullOrWhiteSpace($baseName)) {
# construct the filenames to copy
$pattern = $extensions | ForEach-Object { '{0}.{1}' -f $baseName.Trim(), $_ }
# copy all chosen files to the destination folder
(Get-ChildItem -LiteralPath $SourceFolder -Recurse -File) |
Where-Object { $pattern -contains $_.Name } | ForEach-Object {
# rename the extensions by prefixing it with a dollar sign ($)
$targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
$_ | Copy-Item -Destination $targetFile
# if you want ONLY the .xmr file to be renamed with as .$xmr use this instead:
# if ($_.Extension -eq '.xmr') {
# $targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
# $_ | Copy-Item -Destination $targetFile
# }
# else {
# $_ | Copy-Item -Destination $DestFolder
# }
# remove leftover .XMR and .ATT files
if ($_.Extension -match '\.(xmr|att)$') { $_ | Remove-Item -Force }
}
}
Good day!!
Maybe we can help you with this:
First of all you need to ask for the path and we have two ways to do this (maybe more) but those are the ways I know
The first option is the simple, using the Read-Host Cmdlet:
$path = Read-Host "Please type the path of the folder"
The second option is more complicated and use dotnet framework: (to ask for the destination just change the last two variables with destination instead of sourcePath)
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$box = New-Object System.Windows.Forms.Form
$box.Text = 'Script Execution'
$box.Size = New-Object System.Drawing.Size(300,200)
$box.StartPosition = 'CenterScreen'
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(75,120)
$okButton.Size = New-Object System.Drawing.Size(75,23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$box.AcceptButton = $okButton
$box.Controls.Add($okButton)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(150,120)
$cancelButton.Size = New-Object System.Drawing.Size(75,23)
$cancelButton.Text = 'Cancel'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$box.CancelButton = $cancelButton
$box.Controls.Add($cancelButton)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Please type the path of the files:'
$box.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,40)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$box.Controls.Add($textBox)
$box.Topmost = $true
$box.Add_Shown({$textBox.Select()})
$result = $box.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$sourcePath = $textBox.Text
$sourcePath
}
Once we have the path we can copy the files to do it we can use Copy-item
(I'm not sure if you want to rename the original files or the final files, anyway if you want to rename the last files just use rename-item cmdlet)
$fileList = #(
Get-ChildItem -Path $sourcePath -Recurse -include "*.jpl", "*.eml", "*.html", "*.pdf", "*.cml", "*.xml"
)
foreach ($f in $fileList){
Copy-Item -Path $f.fullname -Destination "$destPath\$f.name"
}
And this should be enough, i'm not really sure about the $fileList part maybe I made some typo, sorry in this case.
Have a nice day!

Archive files from a folder which are older than one week to a sub folder using powershell

I have tried below powershell script to move files older than 7 days from Newfolder to Archive_folder. The script is moving the entire path to the Archive_folder (means its creating folders \Users\529817\New folder in to Archive_folder and then copying files and not zipping the folder) , I need help in copying only files from NewFolder to Archive_folder and zip that folder.
$ArchiveYear = "2018"
$ArchiveMonth = "10"
$ArchiveDay = "10"
$SourcePath = "C:\Users\529817\New folder"
$TargetPath = "C:\Users\529817\New folder\Archive_folder"
$YourDirToCompress = "C:\Users\529817\New folder"
$ZipFileResult = "C:\Users\529817\New folder\Archive_folder\$ArchiveDay$ArchiveMonth.zip"
Get-ChildItem $YourDirToCompress -Directory |
#where { $_.Name -notin $DirToExclude} |
Compress-Archive -DestinationPath $ZipFileResult -Update
$Days = "7"
$LogPath = "C:Users\529817\Temp"
$Date = Get-Date -format yyyy-MM-dd_HH-mm
$TargetFolder = "$TargetPath\$Date"
$LogFile = "$LogPath\ArchiveLog-$date.txt"
$TargetZipFile = "$TargetPath\$Date.zip"
$Activity = "Move files older than $Days days from $SourcePath to $TargetFolder"
Write-Verbose $Activity
$OldFiles = Get-Childitem -Path $SourcePath -recurse | Where-Object {$_.LastWriteTime -lt (get-date).AddDays( - $days)}
$Total = $Oldfiles.Count
$Current = 0
$OldFiles | ForEach {
$Current ++
$Filename = $_.fullname
Write-Progress -Activity $Activity -Status $FileName -PercentComplete ($Current / $Total * 100)
$Split = $FileName -split '\\'
$DestFile = $split[1..($split.Length - 1)] -join '\'
$DestFile = "$TargetFolder\$DestFile"
Try {
$null = New-Item -Path $DestFile -Type File -Force
$Null = Move-Item -Path $FileName -Destination $DestFile -Force -ErrorAction:SilentlyContinue
"Successfully moved $filename to $targetfolder" | add-content $LogFile
}
Catch {
$Err = $_.Exception.Message
Write-Error $Err
"Error moving $filename`: $Err " | add-content $LogFile
}
}
You have two problems here:
Your zip file isn't going where you want it to go
Instead, all of the items which should be in the zip are going where the zip should go.
Let's figure out why this is happening so you can do what you need to get it working.
Problem 1
You have line 10 which looks like this:
Compress-Archive -DestinationPath $ZipFileResult -Update
This creates the Zip file but you don't actually do anything with this file, as in we don't see this $ZipFileResult used again in the code. This is why your zip file isn't showing up where you want it to be.
Problem 2
The end of this script has this big block where you are expressly copying the files to the directory,right where you don't want them.
Try {
$null = New-Item -Path $DestFile -Type File -Force
$Null = Move-Item -Path $FileName -Destination $DestFile -Force -ErrorAction:SilentlyContinue
"Successfully moved $filename to $targetfolder" | add-content $LogFile
}
If you only want to move the Zip file, then you can shorten this whole script. Delete everything from line 19 and on down, which begins with this line.
$OldFiles = Get-Childitem -Path $SourcePath -recurse | Where-Object {$_.LastWriteTime -lt (get-date).AddDays( - $days)}
And instead, add a Move-Item command to copy that $ZipFileResult file over to whichever directory you want it to go.
Finally, there are a number of lines which don't do anythign and can be deleted, like this line $TargetZipFile = "$TargetPath\$Date.zip"

Powershell 2.0 extract certain files from zip (include subdirectories)

Apologies, this question is scattered on the internet but I have yet to find a satisfactory answer that uses only Powershell 2.0 (with .NET v3.5) - no external libraries or programs
I'm using the following code to extract log.txt from ZipFile.zip (no matter log.txt's location)
$Destination = (new-object -com shell.application).NameSpace('C:\ZipExtractDir')
$ZipFile = (new-object -com shell.application).NameSpace('C:\ZipFile.zip')
$Destination.CopyHere(($Zipfile.Items() | where-object {$_.Name -like '*log.txt'}), 1044)
Works if log.txt is in directory root \log.txt
Fails if log.txt is in a subdirectory \Subfolder\log.txt
Fails if referencing the literal (.zip) path
{$_.Name -Like '*Subfolder\log.txt'} (both double & single quotes fail)
Have tried using -eq -like -contains '' "" $_.FullName
I'm quite certain that I'm filtering incorrectly - can anyone help with this code so that it will parse subdirectories as well?
Similar to what you have already done, you can set up the Shell.Application namespaces like this. Then you can copy the extracted directory to the destination path.
$zipFilePath = "Zipfile.zip"
$destinationPath = "C:\Users\Public\Downloads"
$zipfile = (New-Object -Com Shell.Application).NameSpace($zipFilePath)
$destination = (New-Object -Com Shell.Application).NameSpace($destinationPath)
$destination.CopyHere($zipfile.Items())
Then to list the log.txt files, we can contruct the full extracted path with Join-Path. This basically just appends the zip file name from System.IO.Path.GetFileNameWithoutExtension() to the destination path. Then just use Get-ChildItem to list the files recursively with the -Recurse and -Filter switches.
$extractedPath = Join-Path -Path $destinationPath -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($zipFilePath))
Get-ChildItem -Path $extractedPath -Filter log.txt -Recurse
And to test this for PowerShell 2.0 we can use -version 2 with powershell.exe:
powershell.exe -version 2 .\test.ps1
UPDATE
If you want to inspect files before extracting, you'll need to recurse the directories yourself. Below is a demo of how this can be done.
function New-ZipChildRootFolder
{
param
(
[string]$DestinationPath,
[string]$ZipFileName
)
$folderPath = Split-Path -Path $ZipFileName -Leaf
$destination = (New-Object -ComObject Shell.Application).NameSpace($DestinationPath)
$destination.NewFolder($folderPath)
}
function Get-ZipChildItems
{
param
(
[string]$ZipFilePath,
[string]$DestinationPath
)
$zipfile = (New-Object -ComObject Shell.Application).NameSpace($ZipFilePath)
$zipFileName = [System.IO.Path]::GetFileNameWithoutExtension($ZipFilePath)
Write-Output "Create root zip folder : $zipFileName"
New-ZipChildRootFolder -DestinationPath $DestinationPath -ZipFileName $zipFileName
foreach ($item in $zipFile.items())
{
Get-ZipChildItemsRecurse -Items $item -DestinationPath $DestinationPath -ZipFileName $zipFileName
}
}
function Get-ZipChildItemsRecurse
{
param
(
[object]$Items,
[string]$DestinationPath,
[string]$ZipFileName
)
foreach ($file in $Items.getFolder.Items())
{
if ($file.IsFolder -eq $true)
{
Write-Output "Creating folder : $($file.Path)"
New-ZipChildFolder -Folder $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
Get-ZipChildItemsRecurse -Items $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
}
else
{
$filename = Split-Path -Path $file.Path -Leaf
if ($filename -eq "log.txt")
{
Write-Output "Copying file : $($file.Path)"
New-ZipChildFile -File $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
}
}
}
}
function New-ZipChildFile
{
param
(
[object]$File,
[string]$DestinationPath,
[string]$ZipFileName
)
$destination = New-Object -ComObject Shell.Application
$items = $File.Path.Split("\")
$zipRootIndex = [array]::IndexOf($items, $ZipFileName)
$path = $items[$zipRootIndex..($items.Length - 2)] -join "\"
$fullPath = Join-path -Path $DestinationPath -ChildPath $path
$destination.NameSpace($fullPath).CopyHere($File)
}
function New-ZipChildFolder
{
param
(
[object]$Folder,
[string]$DestinationPath,
[string]$ZipFileName
)
$destination = New-Object -ComObject Shell.Application
$items = $Folder.Path.Split("\")
$zipRootIndex = [array]::IndexOf($items, $ZipFileName)
$folders = $items[$zipRootIndex..($items.Length - 1)]
$currentFolder = $DestinationPath
foreach ($folder in $folders)
{
$destination.NameSpace($currentFolder).NewFolder($folder)
$currentFolder = Join-Path -Path $currentFolder -ChildPath $folder
}
}
Usage:
$zipFilePath = "C:\Zipfile.zip"
$destinationPath = "C:\Users\Public\Downloads"
Get-ZipChildItems -ZipFile $zipFilePath -DestinationPath $destinationPath

Extract file from zips in sub folders

I'm relatively new to PowerShell and have only been doing "light" scripting to automate a few tasks at work.
Most of the time, I can Google and get the answer(s) I need, with maybe some minor tweaking/experimenting.
But I need some help with this one as I can't find the answer I'm looking so hoping someone here might be able to help me.
DETAILS:
I am provided a directory link on the network drive to either sub-folders containing ".zips" or a folder with ".zips" only.
The latter case works for my current script (will be provided further down) to do it's job but the former is where I'm struggling.
TL;DR - I need the script to go into each folder, extract each ".zip" into a "Temp" folder and then look for a "backup" image file (file type may vary) and then copy it out to the sub-folder and rename to the same as the ".zip" it was extracted from.
Again, my script works if I'm physically in the folder with the ".zip" files but not if the ".zip" files are in sub-folders as I can't get it recursively go into each folder and run the script.
EXAMPLE SETUP:
FolderA\FolderB\1.zip
FolderA\FolderB\2.zip
FolderA\FolderB\3.zip
FolderA\FolderC\1.zip
FolderA\FolderC\1.zip
FolderA\FolderC\1.zip
Script below:
#Current Directory of ".zip" files
$fileLocation = read-host "Type/Paste location of creatives"
#replace server path with drive letter
if (Test-Path \\server\path\* -PathType Leaf) {
$serverPathName = "\\server\path\"
$driveLetter = "D:\"
$fileLocation = ($fileLocation -replace [regex]::Escape($serverPathName),$driveLetter)
}
$fileLocation = Resolve-Path $fileLocation
Write-Output $fileLocation
#change directory to the one provided
$zipFilesPath = cd "$fileLocation"
$currentDirectory = pwd
#File type of backup
#Note: `n = new line
write-host "`nChoose Creative Backup File Type"
Write-Host "1. JPG"
Write-host "2. JPEG"
Write-Host "3. PNG"
Write-Host "4. GIF"
$typeFilter = Read-Host "`nType? 1/2/3/4"
if($typeFilter -eq '1'){
$typeFilter = 'jpg'
}
elseif($typeFilter -eq '2'){
$typeFilter = 'jpeg'
}
elseif($typeFilter -eq '3'){
$typeFilter = 'png'
}
elseif($typeFilter -eq '4'){
$typeFilter = 'gif'
}
else {
write-host "ERROR! Incorrect Input!"
Write-Host "Exiting Script..."
start-sleep -seconds 1.5
Exit
}
#Files Location
$ZipFilesPath = "$currentDirectory\*.zip"
#Unzip To Same Location
$UnzipPath = "$currentDirectory"
#Check if Temp Folder exists
$TempCheck = Test-Path "$currentDirectory\Temp"
If ($TempCheck -eq $false) {
#Create Temp Folder
New-Item -ItemType directory -Path "$currentDirectory\Temp" -Force
}
$TempPath = "$currentDirectory\Temp"
$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($TempPath)
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
$FileCounter = 1
#Clear Initilisation Vars from Console
clear
foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the extension
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host File: $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1040)
#Find and rename backups
$BackupFiles = Get-ChildItem $TempPath -Filter *backup*.$typeFilter -Recurse
$BackupFiles |% {Move-Item $_.Fullname $UnzipPath/$ZipFileActualName'_backup'.$typefilter}
#Clear Temp Folder
Get-ChildItem -Path "$currentDirectory\Temp" -Include *.* -File -Recurse | foreach { $_.Delete()}
#Move Along to Next File
$FileCounter++
}
#Delete Temp Folder
Remove-Item "$currentDirectory\Temp" -Force -Recurse
#Clear Console
clear
#Read-Host -Prompt “Press Enter to exit”
#start .\
I'm open to any suggestions provided or areas of improvement. If I need to completely change the code, I don't mind. Long as it works for both scenarios.
Thanks.
UPDATE
Modified script as per HAL9256's instruction:
#Current Directory of creatives
$fileLocation = read-host "Type/Paste location of creatives"
if (Test-Path \\server\path\* -PathType Leaf) {
$serverPathName = "\\server\path\"
$driveLetter = "D:\"
$fileLocation = ($fileLocation -replace [regex]::Escape($serverPathName),$driveLetter)
}
$fileLocation = Resolve-Path $fileLocation
Write-Output $fileLocation
$zipFilesPath = cd "$fileLocation"
$currentDirectory = pwd
#File type of backup
#Note: `n = new line
write-host "`nChoose Creative Backup File Type"
Write-Host "1. JPG"
Write-host "2. JPEG"
Write-Host "3. PNG"
Write-Host "4. GIF"
$typeFilter = Read-Host "`nType? 1/2/3/4"
if($typeFilter -eq '1'){
$typeFilter = 'jpg'
}
elseif($typeFilter -eq '2'){
$typeFilter = 'jpeg'
}
elseif($typeFilter -eq '3'){
$typeFilter = 'png'
}
elseif($typeFilter -eq '4'){
$typeFilter = 'gif'
}
else {
write-host "ERROR! Incorrect Input!"
Write-Host "Exiting Script..."
start-sleep -seconds 1.5
Exit
}
#Files Location
$ZipFilesPath = "$currentDirectory"
#Unzip To Same Location
$UnzipPath = "$currentDirectory"
#Check if Temp Folder exists
$TempCheck = Test-Path "$currentDirectory\Temp"
If ($TempCheck -eq $false) {
#Create Temp Folder
New-Item -ItemType directory -Path "$currentDirectory\Temp" -Force
}
$TempPath = "$currentDirectory\Temp"
$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($TempPath)
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
$FileCounter = 1
#Clear Initilisation Vars from Console
clear
foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the extension
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host File: $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1040)
#Find and rename backups
$BackupFiles = Get-ChildItem $TempPath -Filter *backup*.$typeFilter -Recurse
$BackupFiles |% {Move-Item $_.Fullname $upzipPath/$ZipFileActualName'_backup'.$typefilter}
#Clear Temp Folder
Get-ChildItem -Path "$currentDirectory\Temp" -Include *.* -File -Recurse | foreach { $_.Delete()}
#Move Along to Next File
$FileCounter++
}
#Delete Temp Folder
Remove-Item "$currentDirectory\Temp" -Force -Recurse
#Clear Console
clear
#Read-Host -Prompt “Press Enter to exit”
#start .\
That did what I need to do. But how can I modify my script so it stores the backups in their respective folder? So the backups extracted from "FolderB" stay in "FolderB", etc? Is there something I can do?
UPDATE 2
Final code - Thanks to help from HAL9256:
#Current Directory of creatives
$fileLocation = read-host "Type/Paste location of creatives"
if (Test-Path \\server\path\* -PathType Leaf) {
$serverPathName = "\\server\path\"
$driveLetter = "D:\"
$fileLocation = ($fileLocation -replace [regex]::Escape($serverPathName),$driveLetter)
}
$fileLocation = Resolve-Path $fileLocation
Write-Output $fileLocation
$zipFilesPath = cd "$fileLocation"
$currentDirectory = pwd
#File type of backup
#Note: `n = new line
write-host "`nChoose Creative Backup File Type"
Write-Host "1. JPG"
Write-host "2. JPEG"
Write-Host "3. PNG"
Write-Host "4. GIF"
$typeFilter = Read-Host "`nType? 1/2/3/4"
if($typeFilter -eq '1'){
$typeFilter = 'jpg'
}
elseif($typeFilter -eq '2'){
$typeFilter = 'jpeg'
}
elseif($typeFilter -eq '3'){
$typeFilter = 'png'
}
elseif($typeFilter -eq '4'){
$typeFilter = 'gif'
}
else {
write-host "ERROR! Incorrect Input!"
Write-Host "Exiting Script..."
start-sleep -seconds 1.5
Exit
}
#Files Location
$ZipFilesPath = "$currentDirectory"
#Unzip To Same Location
$UnzipPath = "$currentDirectory"
#Check if Temp Folder exists
$TempCheck = Test-Path "$currentDirectory\Temp"
If ($TempCheck -eq $false) {
#Create Temp Folder
New-Item -ItemType directory -Path "$currentDirectory\Temp" -Force
}
$TempPath = "$currentDirectory\Temp"
$Shell = New-Object -com Shell.Application
$Location = $Shell.NameSpace($TempPath)
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
$FileCounter = 1
#Clear Initilisation Vars from Console
clear
foreach ($ZipFile in $ZipFiles) {
#Get The Base Filename without the extension
$ZipFileActualName = [io.path]::GetFileNameWithoutExtension($ZipFile.FullName)
write-host File: $ZipFileActualName
$ZipFolder = $Shell.NameSpace($ZipFile.fullname)
$Location.Copyhere($ZipFolder.items(), 1040)
$DestinationDir = $ZipFile.DirectoryName.Replace($ZipFilesPath,$unzipPath)
#Find and rename backups
$BackupFiles = Get-ChildItem $TempPath -Filter *backup*.$typeFilter -Recurse
$BackupFiles |% {Move-Item $_.Fullname $DestinationDir/$ZipFileActualName'_backup'.$typefilter}
#Clear Temp Folder
Get-ChildItem -Path "$currentDirectory\Temp" -Include *.* -File -Recurse | foreach { $_.Delete()}
#Move Along to Next File
$FileCounter++
}
#Delete Temp Folder
Remove-Item "$currentDirectory\Temp" -Force -Recurse
#Clear Console
clear
#Read-Host -Prompt “Press Enter to exit”
#start .\
When you specify the path:
#Files Location
$ZipFilesPath = "$currentDirectory\*.zip"
Then trying to get the files:
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
What you essentially are saying is Get-Childitem and get only *.zip files. Because the path parameter has the wildcard filter, it will only get all the *.zip files in that directory and will not get any folders. Since you have no folders matching the *.zip wildcard, Get-Childitem won't have any folders to continue to recurse through.
The solution is to only give a base folder to the path parameter for the Get-Childitem to recurse through, and, correctly, use the -Include to apply the filter for the type. e.g.:
#Files Location
$ZipFilesPath = "$currentDirectory"
...
$ZipFiles = Get-Childitem $ZipFilesPath -Recurse -Include *.ZIP
EDIT:
To move the file to the same folder structure, when inside the loop, you simply use the replace function to replace the source Directory path (the first part of the Directory Name) with your destination base path. Then the sub folders will remain the same. e.g.
...
$DestinationDir = $ZipFile.DirectoryName.Replace($ZipFilesPath,$upzipPath)
...
$BackupFiles |% {Move-Item $_.Fullname $DestinationDir/$ZipFileActualName'_backup'.$typefilter}

Most elegant way to extract a directory from a zipfile using PowerShell?

I need to unzip a specific directory from a zipfile.
Like for example extract the directory 'test\etc\script' from zipfile 'c:\tmp\test.zip' and place it in c:\tmp\output\test\etc\script.
The code below works but has two quirks:
I need to recursively find the directory ('script') in the zip file (function finditem) although I already know the path ('c:\tmp\test.zip\test\etc\script')
With CopyHere I need to determine the targetdirectory, specifically the 'test\etc' part manually
Any better solutions? Thanks.
The code:
function finditem($items, $itemname)
{
foreach($item In $items)
{
if ($item.GetFolder -ne $Null)
{
finditem $item.GetFolder.items() $itemname
}
if ($item.name -Like $itemname)
{
return $item
}
}
}
$source = 'c:\tmp\test.zip'
$target = 'c:\tmp\output'
$shell = new-object -com shell.application
# find script folder e.g. c:\tmp\test.zip\test\etc\script
$item = finditem $shell.NameSpace($source).Items() "script"
# output folder is c:\tmp\output\test\etc
$targetfolder = Join-Path $target ((split-path $item.path -Parent) -replace '^.*zip')
New-Item $targetfolder -ItemType directory -ErrorAction Ignore
# unzip c:\tmp\test.zip\test\etc\script to c:\tmp\output\test\etc
$shell.NameSpace($targetfolder).CopyHere($item)
I don't know about most elegant, but with .Net 4.5 installed you could use the ZipFile class from the System.IO.Compression namespace:
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null
$zipfile = 'C:\path\to\your.zip'
$folder = 'folder\inside\zipfile'
$dst = 'C:\output\folder'
[IO.Compression.ZipFile]::OpenRead($zipfile).Entries | ? {
$_.FullName -like "$($folder -replace '\\','/')/*"
} | % {
$file = Join-Path $dst $_.FullName
$parent = Split-Path -Parent $file
if (-not (Test-Path -LiteralPath $parent)) {
New-Item -Path $parent -Type Directory | Out-Null
}
[IO.Compression.ZipFileExtensions]::ExtractToFile($_, $file, $true)
}
The 3rd parameter of ExtractToFile() can be omitted. If present it defines whether existing files will be overwritten or not.
As far as the folder location in a zip is known, the original code can be simplified:
$source = 'c:\tmp\test.zip' # zip file
$target = 'c:\tmp\output' # target root
$folder = 'test\etc\script' # path in the zip
$shell = New-Object -ComObject Shell.Application
# find script folder e.g. c:\tmp\test.zip\test\etc\script
$item = $shell.NameSpace("$source\$folder")
# actual destination directory
$path = Split-Path (Join-Path $target $folder)
if (!(Test-Path $path)) {$null = mkdir $path}
# unzip c:\tmp\test.zip\test\etc\script to c:\tmp\output\test\etc\script
$shell.NameSpace($path).CopyHere($item)
Windows PowerShell 5.0 (included in Windows 10) natively supports extracting ZIP files using Expand-Archive cmdlet:
Expand-Archive -Path Draft.Zip -DestinationPath C:\Reference