I want to create a zip file in powershell, add items to the zip file, then get the compressed content of that zip file as bytes immediately after the zip file is created, in the same scipt. The problem is that it does not seem that the zip application has written its contents to the file system. How does one close/flush the zip application so that the next powershell statement can gain access to the newly created zip file?
Example:
new-item -type File test.zip -force
$zip = ( get-item test.zip ).fullname
$app = new-object -com shell.application
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname )
dir test.zip # <---- Empty test.zip file
Get-Content -Encoding byte $zip | echo # <-- nothing echoed
The "dir test.zip" shows a zip file with no contents, thus the Get-Content returns nothing.
Please note that this seems to be a problem with the asynchronous behavior of the copyhere action. If I sleep after the copyhere line, the zip file will become populated. However, I do not know how long one must sleep, nor do I want to delay the processing.
Much Thanks in advance!
You might want to reconsider using a third party library. However, if you must use copyhere, try this:
new-item -type File test.zip -force
$zip = ( get-item test.zip ).fullname
$app = new-object -com shell.application
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname)
while($folder.Items().Item($item.Name) -Eq $null)
{
start-sleep -seconds 0.01
write-host "." -nonewline
}
dir test.zip # <---- Empty test.zip file
Get-Content -Encoding byte $zip
I also had this problem, all that you need is to wait until zipping operation is not completed. So, i come up with this solution, you should place this code after executing "$folder.copyhere" or "Copy-ToZip" powerpack cmdlet.
$isCompleted = $false
$guid = [Guid]::NewGuid().ToString()
$tmpFileName = $zipFileName + $guid
# The main idea is to try to rename target ZIP file. If it success then archiving operation is complete.
while(!$isCompleted)
{
start-sleep -seconds 1
Rename-Item $zipFileName $tmpFileName -ea 0 #Try to rename with suppressing errors
if (Test-Path $tmpFileName)
{
Rename-Item $tmpFileName $zipFileName #Rename it back
$isCompleted = $true
}
}
This method to zip files is not appropriate for automated scripts. This has limitations in Windows 2003 and Windows xp server for 3 gigs. Also, it does not give proper errors.
Create the file in a closure, so that the variable goes out of scope and powershell closes the file... if you'd made that part into a subroutine then it would have closed naturally when you returned.
new-item -type File test.zip -force
{
$zip = ( get-item test.zip ).fullname
$app = new-object -com shell.application
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname )
}
dir test.zip # <---- Empty test.zip file
Get-Content -Encoding byte $zip
try creating the zip empty file like this:
$zipfilename = "myzip.zip"
set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
Then the rest of your code.
this code in my box works:
$zipfilename = "a.zip"
set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
$app = new-object -com shell.application
$zip = ( get-item a.zip ).fullname
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname )
to get the content of a zip use this:
$zipfilename = "myzip.zip"
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipfilename)
$zipPackage.Items() | Select Path
Related
and thank you in advance!!! I was able to successfully move documents from one server(sharepoint) to another (pulse). However, each document begins with HC_.jpg.
How can I move the document and rename at the same time. Currently I am using two separate scripts and two separate scheduled tasks which is less than ideal.
First Script that moves the document from SharePoint Server:
######################## Start Variables ########################
######################## Adam's Script######################
$destination = "\\pulse-dev.domain.com\C$\ProfilePhotos\"
$webUrl = "http://mysites-dev.domain.com"
$listUrl = "http://mysites-dev.domain.com/user photos/"
##############################################################
$web = Get-SPWeb -Identity $webUrl
$list = $web.GetList($listUrl)
function ProcessFolder {
param($folderUrl)
$folder = $web.GetFolder($folderUrl)
foreach ($file in $folder.Files) {
#Ensure destination directory
$destinationfolder = $destination + "/" + $folder.Url
if (!(Test-Path -path $destinationfolder))
{
$dest = New-Item $destinationfolder -type directory
}
#Download file
$binary = $file.OpenBinary()
$stream = New-Object System.IO.FileStream($destinationfolder + "/" + $file.Name), Create
$writer = New-Object System.IO.BinaryWriter($stream)
$writer.write($binary)
$writer.Close()
}
}
#Download root files
ProcessFolder($list.RootFolder.Url)
#Download files in folders
#foreach ($folder in $list.Folders) {
#ProcessFolder($folder.URL)
#}
Second Script that is run on the Pulse server after first script completes
$dir= "C:\ProfilePhotos\"
CD $dir
Get-ChildItem -Recurse |
where-Object {$_.Name -match 'HC_'} |
Rename-Item -NewName {$_.Name -replace 'HC_', ''}
Replace
$stream = New-Object System.IO.FileStream($destinationfolder + "/" + $file.Name), Create
with
$stream = New-Object System.IO.FileStream($destinationfolder + "/" + ($file.Name -replace "^HC_")), Create
$source = "C:\folder\*.*"
$destination = "C:\folder3\test.zip"
$results = (Get-ChildItem -Path $source -Filter *.* | ? {
$_.LastWriteTime -gt (Get-Date).AddDays(-1))
}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($results, $destination)
Now i need to select last modified files and zip either by compress or 7zip any help ?
To zip files via Powershell, I usually install 7-zip on the system and then include this function in the script:
#7-ZIP FILE FUNCTION (must install 7-zip on system)
#-------------------------------------------------------------------------------------
#
function ZipFile7 ([string] $pZipFile, [string] $pFiles) {
$7zipExe = "$($env:programfiles)\7-Zip\7z.exe"
$7zArgs = #("a", "-tzip", $pZipFile, $pFiles, "-r")
& $7zipExe $7zArgs
}
#-------------------------------------------------------------------------------------
$pZipFile is the full path of the file you wish to create, and $pFiles is the path to the files you wish to zip (can include wildcards like C:\*.txt).
$source = "C:\folder\*.*"
$destination = "C:\folder3\test.zip"
$results = Get-ChildItem -Path $source | Where {$_.LastWriteTime -gt (Get-Date).AddDays(-1)} | Select -ExpandProperty "FullName"
set-content $destination ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
$Shell = New-Object -Com Shell.Application
$ZipFile = $Shell.Namespace($destination)
$Results | ForEach {
$Count = $ZipFile.Items().Count
$ZipFile.CopyHere($_)
While ($ZipFile.Items().Count -eq $Count) {Start-Sleep -Milliseconds 200} # Wait till the file is zipped
}
Note: you might want to improve the while/wait statement (e.g. set a timeout) as it might hang if anything goes wrong with copying the file to the zip folder.
I am trying to unzip a file, this file has 3 more files inside it which are also zipped. I would like to unzip them all into one single folder. I have unzip script which i am calling from my main script.
My main script is as follows
$localPath = 'C:\Projects\Deployments\'
$latestDir = $(get-childitem $localPath | sort lastwritetime | select -Last 1).FullName
$Unzip = 'C:\Projects\unzip'
.\unzip.ps1 $latestDir $Unzip
$src = $unzip + "\"
#The below two zip file are contained within 1 more main zip file which is called package
$TabletzipPath = $Unzip + "tablet-webdeploy-3.0.zip"
$AdminzipPath = $Unzip + "admin-webdeploy-3.0.zip"
$Date = Get-Date
$folder_date = $Date.ToString("yyyy-MM-dd_HHmm")
$tempPath = 'C:\_Temp' + $folder_date
if (!(Test-Path -path $tempPath))
{
New-Item $tempPath -type directory
}
.\unzip.ps1 $TabletzipPath $tempPath
.\unzip.ps1 $AdminzipPath $tempPath
My unzip file is as follows
function Expand-ZIPFile($file, $destination)
{
$shell = new-object -com shell.application
$zip = $shell.NameSpace($file)
foreach($item in $zip.items())
{
$shell.Namespace($destination).copyhere($item)
}
}
Expand-ZIPFile -File $args[0] -Destination $args[1]
The contents of the zip file are exposed in your function variable $zip.items(). You can filter it to find anything ending in .zip, add that to an array then once the main unzip is complete, if the array exists you can use it to continue unzipping things. You can call the function into your current scope by dot sourcing it to see the variable.
($Zip.items()) | select -expand name | ? {$_ -like "*.zip"}
Alternatively you could always complete your script, scan the extract directory for things ending in .zip and then process them if any are found.
hi i am trying to unzip folders and sub folders i have tried different ways and script but no luck.
Can some one help on this
$Date = Get-Date
#
$folder_date = $Date.ToString("yyyy-MM-dd_HHmmss")
$content = get-childitem 'C:\Deployments'
$sortedContent = $content | Sort-Object LastWriteTime -Descending
# 1. LIST BACKUPS
write-host "This is the list of all the Deployments :"
$count = 1
foreach ($item in $sortedContent)
{
#Edit: Now with auto-incrementing counter
Write-Host ("{0}: {1}" -f $count, $item.Name)
$count++
}
$itemNumber = Read-Host "Please select which deployments you want to copy "
$itemNumber = $itemNumber -1
if($sortedContent[$itemNumber].PSIsContainer -eq $true)
{
$src = $sortedContent[$itemNumber].FullName + "\"
$Date = Get-Date
$folder_date = $Date.ToString("yyyy-MM-dd_HHmm")
$tempPath = 'C:\_Temp\Restore' + $folder_date
if (!(Test-Path -path $tempPath))
{
New-Item $tempPath -type directory
}
$TabletzipPath = $src + "table.zip"
$AdminzipPath = $src + "admin.zip"
.\unzip.ps1 $src $tempPath
.\unzip.ps1 $TabletzipPath $tempPath
.\unzip.ps1 $AdminzipPath $tempPath
}
$Shell = new-object -com shell.application
$Zip = $Shell.NameSpace($PathToZip)
ForEach($Item in $Zip.items())
{
$Shell.Namespace($ExtractPath).copyhere($Item)
}
If your just looking for an easy & quick way of doing this.
You can use this function:
Unzip-File -File C:\location.zip -Destination E:\destination
Need this script:
http://gallery.technet.microsoft.com/scriptcenter/PowerShell-Function-to-727d6200
Using jar unzipping: copy-paste it into PowerShell ISE or create a .ps1 file with this code:
#choose directory where your script is
cd $PSScriptRoot
#do for each file in your directory, -recurse stands for all subfolders, *.jar stands for all files with jar extension
foreach ($file in (dir -Recurse *.jar))
{
#Uncomment this to check files this script is going to unzip (don't forget to comment 7z line below to avoid operation)
#Write-Host ("Dir for file " + $file.Name + " is " + $file.Directory)
#put here full path to 7z if there is no system env (or just make it) for that
#x stands for eXtract files with full paths
#-o is for output folder (example of usage -oc:\test\)
7z x $file.FullName ("-o"+$file.Directory)
}
It should unzip everything you need directly to the folder where your .jar (or any other archive) is.
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