Remove Folder from ZIP file created by Power Shell Community Extensions - powershell

All,
I am using Power Shell Community Extensions for PSv1 and the ZIP file is being created correctly. However, I only want the images in the ZIP file and I want to remove the folder from the ZIP file.
I have the folder called newpictures that is zipped up. I then use the -flattenpaths option in the Power Shell Community Extensions to put all the images in the base path, but the folder remains.
I have been searching online for a solution. I am not sure if I have this right, so can someone look over this code and let me know if this is correct before I proceed?
if (test-path $PSCXInstallDir) {
write-zip -path "Microsoft.PowerShell.Core\FileSystem::$TestSite" -outputpath "Microsoft.PowerShell.Core\FileSystem::$ZipFileCreationDir\$ZipFileName" -noclobber -quiet -flattenpaths -level 9
start-sleep -seconds 30
if (test-path $ZipFileCreationDir\$ZipFileName) {
$ShellApp = new-object -com shell.application
$TheZipFile = $ShellApp.namespace("$ZipFileCreationDir\$ZipFileName")
$TheZipFile.items() | where-object {$_.name -eq $FolderToCompress} | remove-item $FolderToCompress
}
}
The variables are:
$PSCXInstallDir = "C:\Program Files\PowerShell Community Extensions"
$TestSite = "\\10.0.100.3\www2.varietydistributors.com\catalog\newpictures"
$ZipFileCreationDir = "\\10.0.100.3\www2.varietydistributors.com\catalog"
$ZipFileName = "vdi-test.zip"
$FolderToCompress = "newpictures"
Thanks in advance for any feedback. In short, I just want to remove the single folder within the ZIP file.

Remove-Item won't work on items inside the zip file. You need to move the objects you want to delete outside the zip file before you can delete them:
$ShellApp = New-Object -COM 'Shell.Application'
$TheZipFile = $ShellApp.NameSpace("$ZipFileCreationDir\$ZipFileName")
$TheZipFile.Items() | ? { $_.Name -eq $FolderToCompress } | % {
$ShellApp.NameSpace($env:TEMP).MoveHere($_)
Remove-Item (Join-Path $env:TEMP $_.Name)
}
Note that Items() doesn't recurse into nested folders, it only enumerates the files and folders of the current namespace. If you need to process contents of nested folders you need to specify the nested path:
$NestedFolder = $ShellApp.NameSpace('C:\path\to\your.zip\nested\folder')
or recurse with something like this:
function RecurseIntoZip($fldr) {
$fldr.Items() | ? { $_.Name -eq $FolderToCompress } | % {
$_.Name
}
$fldr.Items() | ? { $_.Type -eq 'File folder' } | % {
RecurseIntoZip $_.GetFolder
}
}
RecurseIntoZip $TheZipFile

Related

Move only New files to a different directory - Powershell

Running the following in powershell. Goal is to move files from folder 2 to folder 1, but only new files. Also would like to know which files were moved, sort like a log file if possible
Here is what i have so far :
$DownloadFolder = 'C:\FOLDER1'
$KeepFolder = 'C:\folder2'
$DownloadFiles = Get-ChildItem -Path $DownloadFolder
$KeepFiles = Get-ChildItem -Path $KeepFolder
$FileDiffs = Compare-Object -ReferenceObject $DownloadFiles -DifferenceObject $KeepFiles
$FileDiffs | foreach {
$copyParams = #{
'path' = $_.InputObject.Fullname
}
$Downloadll = $copyParams.path
if ($_.SideIndicator -eq '=>')
{
Copy-Item $Downloadll -Destination $KeepFolder -force
}
}
Trying to compare folder 1 and folder 2 and move ONLY new files to folder1. But getting the following error:
"" Cannot overwrite with the item filename.txt with itself
I would look into using the "robocopy" command.
It can move the newer files only, and it gives you the log you wanted.
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
https://serverfault.com/questions/129098/how-to-get-robocopy-running-in-powershell
There is a colon after $downloadfolder on the first line.
It's spelled wrong also.
You want <= not =>. Otherwise it tries to copy to the first folder.
Add -passthru to copy-item to get the output.

Compare directories exactly - including moved files

My aim is to compare two directories exactly - including the structure of the directories and sub-directories.
I need this, because I want to monitor if something in the folder E:\path2 was changed. For this case a copy of the full folder is in C:\path1. If someone changes something it has to be done in two directories.
It is important for us, because if something is changed in the directory (accidentally or not) it could break down other functions in our infrastructure.
This is the script I've already written:
# Compare files for "copy default folder"
# This Script compares the files and folders which are synced to every client.
# Source: https://mcpmag.com/articles/2016/04/14/contents-of-two-folders-with-powershell.aspx
# 1. Compare content and Name of every file recursively
$SourceDocsHash = Get-ChildItem -recurse –Path C:\path1 | foreach {Get-FileHash –Path $_.FullName}
$DestDocsHash = Get-ChildItem -recurse –Path E:\path2 | foreach {Get-FileHash –Path $_.FullName}
$ResultDocsHash = (Compare-Object -ReferenceObject $SourceDocsHash -DifferenceObject $DestDocsHash -Property hash -PassThru).Path
# 2. Compare name of every folder recursively
$SourceFolders = Get-ChildItem -recurse –Path C:\path1 #| where {!$_.PSIsContainer}
$DestFolders = Get-ChildItem -recurse –Path E:\path2 #| where {!$_.PSIsContainer}
$CompareFolders = Compare-Object -ReferenceObject $SourceFolders -DifferenceObject $DestFolders -PassThru -Property Name
$ResultFolders = $CompareFolders | Select-Object FullName
# 3. Check if UNC-Path is reachable
# Source: https://stackoverflow.com/questions/8095638/how-do-i-negate-a-condition-in-powershell
# Printout, if UNC-Path is not available.
if(-Not (Test-Path \\bb-srv-025.ftscu.be\DIP$\Settings\ftsCube\default-folder-on-client\00_ftsCube)){
$UNCpathReachable = "UNC-Path not reachable and maybe"
}
# 4. Count files for statistics
# Source: https://stackoverflow.com/questions/14714284/count-items-in-a-folder-with-powershell
$count = (Get-ChildItem -recurse –Path E:\path2 | Measure-Object ).Count;
# FINAL: Print out result for check_mk
if($ResultDocsHash -Or $ResultFolders -Or $UNCpathReachable){
echo "2 copy-default-folders-C-00_ftsCube files-and-folders-count=$count CRITIAL - $UNCpathReachable the following files or folders has been changed: $ResultDocs $ResultFolders (none if empty after ':')"
}
else{
echo "0 copy-default-folders-C-00_ftsCube files-and-folders-count=$count OK - no files has changed"
}
I know the output is not perfect formatted, but it's OK. :-)
This script spots the following changes successfully:
create new folder or new file
rename folder or file -> it is shown as error, but the output is empty. I can live with that. But maybe someone sees the reason. :-)
delete folder or file
change file content
This script does NOT spot the following changes:
move folder or file to other sub-folder. The script still says "everything OK"
I've been trying a lot of things, but could not solve this.
Does anyone can help me how the script can be extended to spot a moved folder or file?
I think your best bet is to use the .NET FileSystemWatcher class. It's not trivial to implement an advanced function that uses it, but I think it will simplify things for you.
I used the article Tracking Changes to a Folder Using PowerShell when I was learning this class. The author's code is below. I cleaned it up as little as I could stand. (That publishing platform's code formatting hurts my eyes.)
I think you want to run it like this.
New-FileSystemWatcher -Path E:\path2 -Recurse
I could be wrong.
Function New-FileSystemWatcher {
[cmdletbinding()]
Param (
[parameter()]
[string]$Path,
[parameter()]
[ValidateSet('Changed', 'Created', 'Deleted', 'Renamed')]
[string[]]$EventName,
[parameter()]
[string]$Filter,
[parameter()]
[System.IO.NotifyFilters]$NotifyFilter,
[parameter()]
[switch]$Recurse,
[parameter()]
[scriptblock]$Action
)
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
If (-NOT $PSBoundParameters.ContainsKey('Path')){
$Path = $PWD
}
$FileSystemWatcher.Path = $Path
If ($PSBoundParameters.ContainsKey('Filter')) {
$FileSystemWatcher.Filter = $Filter
}
If ($PSBoundParameters.ContainsKey('NotifyFilter')) {
$FileSystemWatcher.NotifyFilter = $NotifyFilter
}
If ($PSBoundParameters.ContainsKey('Recurse')) {
$FileSystemWatcher.IncludeSubdirectories = $True
}
If (-NOT $PSBoundParameters.ContainsKey('EventName')){
$EventName = 'Changed','Created','Deleted','Renamed'
}
If (-NOT $PSBoundParameters.ContainsKey('Action')){
$Action = {
Switch ($Event.SourceEventArgs.ChangeType) {
'Renamed' {
$Object = "{0} was {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath,
$Event.SourceEventArgs.ChangeType,
$Event.SourceArgs[-1].FullPath,
$Event.TimeGenerated
}
Default {
$Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath,
$Event.SourceEventArgs.ChangeType,
$Event.TimeGenerated
}
}
$WriteHostParams = #{
ForegroundColor = 'Green'
BackgroundColor = 'Black'
Object = $Object
}
Write-Host #WriteHostParams
}
}
$ObjectEventParams = #{
InputObject = $FileSystemWatcher
Action = $Action
}
ForEach ($Item in $EventName) {
$ObjectEventParams.EventName = $Item
$ObjectEventParams.SourceIdentifier = "File.$($Item)"
Write-Verbose "Starting watcher for Event: $($Item)"
$Null = Register-ObjectEvent #ObjectEventParams
}
}
I don't think any example I've found online tells you how to stop watching the filesystem. The simplest way is to just close your PowerShell window. But I always seem to have 15 tabs open in each of five PowerShell windows, and closing one of them is a nuisance.
Instead, you can use Get-Job to get the Id of registered events. Then use Unregister-Event -SubscriptionId n to, well, unregister the event, where 'n' represents the number(s) you find in the Id property of Get-Job..
So basically you want to synchronize the two folders and note all the changes made on that:
I would suggest you to use
Sync-Folder Script
Or
FreeFile Sync.

How do I select files in a folder based on part of filename and zip them in Powershell?

I'm fairly new to Powershell(using Powershell 2.0 btw) and am trying to make a script that does several things(this is my 3rd script or so). I have most things in place but the last thing remaining is to group files of different types (xml, tfw and tif) in a folder, based on the first part of the filename(first three characters) and then zip these files into several zip-files with name like the first 3 characters, either in the same location or in a new one.
Sample of folder content:
001.tif
001.tfw
001.metadata.xml
002.tif
002.tfw
002.metadata.xml
003.tif
003.tfw
003.metadata.xml
003_svel.tif
003_svel.tfw
003_svel.metadata.xml
Wanted result:
001.zip containing 001.tif, 001.tfw, 001.metadata.xml
002.zip containing 002.tif, 002.tfw, 002.metadata.xml
003.zip containing 003.tif, 003.tfw, 003.metadata.xml, 003_svel.tif,
003_svel.tfw and 003_svel.metadata.xml
I have installed 7-zip to do the zipping and am using the commandline version. I've used 7-zip local on some testfiles and got it to work, but then it was only tif-files. I have a source folder where I search for the latest created folder and then process the files in it.
This is what I have so far(Powershell 2.0):
$dir_source = "c:\Test"
$new_folder = Get-ChildItem $dir_source -Recurse |
Where { $_.PSIsContainer} |
Sort-Object LastWriteTime -Descending |
Select-Object -ExpandProperty Fullname <-First 1
Get-ChildItem $new_folder -recurse -Exclude metafile.xml |
Group-Object {$_.Name.Substring(0,3)}
This gives me a list of grouped files in the lates created folder based on the first 3 characters in the filename. It also show what files are in each group.
Like below:
Count Name Group
----- ---- -----
3 003 {C:\Test\20150708 063255_B\003.metafile.xml, C:\Test\20150708 063255_B\003.tfw, C:\Test\20150708 063255_B\003.tif}
6 004 {C:\Test\20150708 063255_B\004.metafile.xml, C:\Test\20150708 063255_B\004.tfw, C:\Test\20150708 063255_B\004.tif,C:\Test...
6 009 {C:\Test\20150708 063255_B\009.metafile.xml, C:\Test\20150708 063255_B\009.tfw, C:\Test\20150708 063255_B\009.tif,C:\Test...
Now my next step ist to take these groups and zip them. Ideally create these zip-files in a different destination directory (I believe I can change this when setting the $directory- variable in the script below.)
foreach ($group in $dataset) {
$name = $file.name
$directory = $file.DirectoryName
$zipFile = $file.Name + ".zip"
sz a -t7z "$directory\$zipfile" "$directory\$name"
This last code is causing some trouble. I either get the message:
7-Zip (A) 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18 Error:
c:\Test\Dest_test460.zip is not supported archive System error:
Incorrect function.
,or
WARNING: Cannot find 1 file 7-Zip (A) 9.20 Copyright (c) 1999-2010
Igor Pavlov 2010-11-18 Scanning \460: WARNING: The system cannot
find the file specified.
,or it starts zipping all files on my userprofile into a zip-file. Depending on changes I do to the $group-value. I believe there are one ore more basic errors in my script causing this, and this is where I'm asking for some help. It may be that I am approaching this the wrong way by first grouping the files I want and then try to zip them?
Anyone that can see my error or give me some hint to what I have to do?
Thanks for your time!
Lee Holmes New-ZipFile do the job, he has two versions one of them using the ICSharpCode.SharpZipLib.dll to compress, and the other not require it, i wrapped the 2nd one into a function:
Function New-ZipFile {
param(
## The name of the zip archive to create
$Path = $(throw "Specify a zip file name"),
## Switch to delete the zip archive if it already exists.
[Switch] $Force
)
Set-StrictMode -Version 3
## Create the Zip File
$zipName = $executionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
## Check if the file exists already. If it does, check
## for -Force - generate an error if not specified.
if(Test-Path $zipName)
{
if($Force)
{
Remove-Item $zipName -Force
}
else
{
throw "Item with specified name $zipName already exists."
}
}
## Add the DLL that helps with file compression
Add-Type -Assembly System.IO.Compression.FileSystem
try
{
## Open the Zip archive
$archive = [System.IO.Compression.ZipFile]::Open($zipName, "Create")
## Go through each file in the input, adding it to the Zip file
## specified
foreach($file in $input)
{
## Skip the current file if it is the zip file itself
if($file.FullName -eq $zipName)
{
continue
}
## Skip directories
if($file.PSIsContainer)
{
continue
}
$item = $file | Get-Item
$null = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
$archive, $item.FullName, $item.Name)
}
}
finally
{
## Close the file
$archive.Dispose()
$archive = $null
}
}
To use it for example:
dir c:\folder -Recurse | New-ZipFile -Path c:\temp\folder.zip
The Source file(for the one that use the ICSharpCode): http://poshcode.org/2202
Use my Previous Answer Function New-ZipFile and use with this one:
$FolderName = "C:\temp"
$Files = dir $FolderName
$prfx = #()
foreach ($file in $files)
{
$prfx += $file.Name.Substring(0,3)
}
$prfx = $prfx | Group
foreach ($Prf in $prfx)
{
$prf = $prf.name.ToString()
dir $Files | ? {$_.Name -match "^$prf"} | New-ZipFile -Path "$foldername\$prf.zip"
}
According to your example It will output 3 zip files like you want,
it will always use the first 3 letters of the file, you can the change this in this line $prfx += $file.Name.Substring(0,3) and set it different if needed.
Good Luck
Could not get the suggested solution to work, had problems configuring ICSharpCode. I also wanted to use 7zip, since it is still under some updating regime.
Ended up copying my files to temp folders based on the filenames and then zip each folder. After that delete the tempfolders with files. Ugly code, but it does the job.
# Create folder based on filename and copy files into respective folder
Get-ChildItem $new_folder -Filter *.* | Where-Object {!$_.PSIsContainer} | Foreach-Object{
$dest = Join-Path $_.DirectoryName $_.Name.SubString(0,3)
if(!(Test-Path -Path $dest -PathType Container))
{
$null = md $dest
}
$_ | Copy-Item -Destination $dest -Force
}
# Create zip-file of each folder
dir $new_folder | Where-Object { $_.PSIsContainer } | ForEach-Object { sz a -t7z -mx9 "$dir_dest\$_.zip" $_.FullName }
# Delete temp-folders
dir $new_folder | Where-Object { $_.PSIsContainer } | Remove-Item -Recurse

Trying to extract just .sql files from zip powershell

I am trying to search thru a zip file and just extract all of the .sql files to a directory. I can make it extract all the files, but there are over 200 misc files in the zip, and I only need the 6 .sql's. Is there an easy way to designate just the .sql?
Here is the example code that I was trying to get to work, if there is a better way, I would love to hear.
$shell = new-object -com shell.application
$zip = $shell.NameSpace(“C:\Temp”)
foreach($item in $zip.items()){
if([System.IO.Path]::GetExtension($item.Path) -eq ".sql"){
$shell.Namespace(“C:\Project\”).copyhere($item)
}
}
If you have (or grab) the PowerShell Community Extensions, you can use its archive commands:
Read-Archive C:\temp\foo.zip | %{$_} | Where Name -match '\.sql' | Expand-Archive
If you are on PowerShell V3 on a system with .NET 4.5 installed, you can use the System.IO.Compression.ZipFile class to extract the sql files.
Add-Type -Assembly system.io.compression.filesystem
[IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath)
I'd simplify it a little and use variables instead of the string literals, like this:
$shell = New-Object -COM 'Shell.Application'
$zipfile = 'C:\Temp\some.zip'
$destination = 'C:\Project'
$zip = $shell.NameSpace($zipfile)
$zip.Items() | ? { $_.Path -like '*.sql' } | % {
$shell.NameSpace($destination).CopyHere($_)
}
but other than that your code should do just fine.
Note, however, that it won't recurse into nested folders inside the zip file. You need something like this for processing nested folders as well:
function ExtractZip($fldr, $dst) {
$fldr.Items() | ? { $_.Path -like '*.sql' } | % {
$shell.NameSpace($dst).CopyHere($_)
}
$fldr.Items() | ? { $_.Type -eq 'File folder' } | % {
ExtractZip $_.GetFolder $dst
}
}
ExtractZip $shell.NameSpace($zipfile) $destination

How to use an array in a zip function using powershell?

I am still pretty new to scripting and "programming" at all. if you miss any information here let me know.
This is my working zip function:
$folder = "C:\zipthis\"
$destinationFilePath = "C:\_archive\zipped"
function create-7zip{
param([string] $folder,
[String] $destinationFilePath)
write-host $folder $destinationFilePath
[string]$pathToZipExe = "C:\Program Files (x86)\7-Zip\7zG.exe";
[Array]$arguments = "a", "-tzip", "$destinationFilePath", "$folder";
& $pathToZipExe $arguments;
}
Get-ChildItem $folder | ? { $_.PSIsContainer} | % {
write-host $_.BaseName $_.Name;
$dest= [System.String]::Concat($destPath,$_.Name,".zip");
(create-7zip $_.FullName $dest)
}
create-7zip $folder $destinationFilePath
now I want him to zip special folders which I already sorted out :
get-childitem "C:\zipme\" | where-Object {$_.name -eq "www" -or $_.name -eq "sql" -or $_.name -eq "services"}
This small function finds the 3 folders I need called www, sql and services. But I didn't manage to insert this into my zip function, so that exactly this folders are zipped and put into C:\_archive\zipped
Because a string is used instead of an array, he tried always to look for a folder called wwwsqlservice which is not there. I tried to put an array using #(www,sql,services) but i had no success, so whats the right way, if there is one?
It should compatible with powershell 2.0, no ps3.0 cmdlets or functions please.
thanks in advance!
Here's a really simple example of what you want to do, removed from the context of your function. It assumes that your destination folders already exist (You can just use Test-Path and New-Item to create them if they don't), and that you're using 7z.exe.
$directories = #("www","sql","services")
$archiveType = "-tzip"
foreach($dir in $directories)
{
# Use $dir to update the destination each loop to prevent overwrites!
$sourceFilePath = "mySourcePath\$dir"
$destinationFilePath = "myTargetPath\$dir"
cmd /c "$pathToZipExe a $archiveType $destinationFilePath $sourceFilePath"
}
Overall it looks like you got pretty close to a solution, with some minor changes needed to support the foreach loop. If you're confident that create-7zip works fine for a single folder, you can substitute that for the cmd /c line above. Here's a listing of some handy example usages for 7zip on the command line.