find all specific folders and zip them - powershell

I want to find all folders with name 'test' and to zip them into single folder with different names, of course.
I managed to do some code:
$RootFolder = "E:\"
$var = Get-ChildItem -Path $RootFolder -Recurse |
where {$_.PSIsContainer -and $_.Name -match 'test'}
#this is assembly for zip functionality
Add-Type -Assembly "System.IO.Compression.Filesystem"
foreach ($dir in $var) {
$destination = "E:\zip\test" + $dir.Name + ".zip"
if (Test-Path $destination) {Remove-Item $destination}
[IO.Compression.Zipfile]::CreateFromDirectory($dir.PSPath, $destination)
}
It gives me an error:
Exception calling "CreateFromDirectory" with "2" argument(s): "The given path's format is not supported."
I want to know, what is the right way to pass path of my $dir.

The PSPath property returned from Get-ChildItem is prefixed with the PSProvider. The CreateFromDirectory() method takes two strings; the first is sourceDirectoryName for which you could use Fullname from your object.
$RootFolder = "E:\"
$Directories = Get-ChildItem -Path $RootFolder -Recurse | Where-Object {
$_.PSIsContainer -And
$_.BaseName -Match 'test'
}
Add-Type -AssemblyName "System.IO.Compression.FileSystem"
foreach ($Directory in $Directories) {
$Destination = "E:\zip\test$($Directory.name).zip"
If (Test-path $Destination) {
Remove-Item $Destination
}
[IO.Compression.ZipFile]::CreateFromDirectory($Directory.Fullname, $Destination)
}

If you're using v5, I would suggest using the Commandlet
If you don't want to use the commandlet you can use this:
$FullName = "Path\FileName"
$Name = CompressedFileName
$ZipFile = "Path\ZipFileName"
$Zip = [System.IO.Compression.ZipFile]::Open($ZipFile,'Update')
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Zip,$FullName,$Name,"optimal")
$Zip.Dispose()

If you have a folder structure like this:
- Folder1
-- Test
- Folder2
-- Test
- Folder3
-- Test
You can do this:
gci -Directory -Recurse -Filter 'test*' | % {
Compress-Archive "$($_.FullName)\**" "$($_.FullName -replace '\\|:', '.' ).zip"
}
And you will get:
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder1.Test.zip
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder2.Test.zip
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder3.Test.zip
Or if you want to preserve the directory structure inside your zips:
gci -Directory -Recurse -Filter 'test*' | % {
Compress-Archive $_.FullName "$($_.FullName -replace '\\|:', '.' ).zip"
}

Related

PowerShell - Find duplicate file inside ZIPs and CABs

I am trying to write a script that will find duplicate file inside a compressed files.
The compressed files can be ZIP or CAB (Need help to extract CAB file also because currently its not working).
What I have so far is to extract the zips to a temp folder (don't know how to extract cab) and if there is a vip inside I need to extract him also to the same folder. currently all the files are extracted to the same temp folder what I need is to extract each zip/cab into a folder with the original name even if he has a vip inside. (the zip/cab are not flat) in the next step I need to find duplication files and display all the duplication and where they found.
The script below is not working...
$tempFolder = Join-Path ([IO.Path]::GetTempPath()) (New-GUID).ToString('n')
$compressedfiles = Get-ChildItem -path C:\Intel\* -Include "*.zip","*.CAB"
foreach ($file in $compressedfiles) {
if ($file -like "*.zip") {
$zip = [System.IO.Compression.ZipFile]::ExtractToDirectory($file, $tempFolder)
$test = Get-ChildItem -path $tempFolder\* -Include "*.vip"
if ($test) {
$zip = [System.IO.Compression.ZipFile]::ExtractToDirectory($test, $tempFolder)
}
}
}
$Files=gci -File -Recurse -path $tempFolder | Select-Object -property FullName
$MatchedSourceFiles=#()
ForEach ($SourceFile in $Files)
{
$MatchingFiles=#()
$MatchingFiles=$Files |Where-Object {$_.name -eq $SourceFile.name}
if ($MatchingFiles.Count -gt 0)
{
$NewObject=[pscustomobject][ordered]#{
File=$SourceFile.FullName
#MatchingFiles=$MatchingFiles
}
$MatchedSourceFiles+=$NewObject
}
}
$MatchedSourceFiles
Remove-Item $tempFolder -Force -Recurse
Building on what you have already tried, you could do this like:
Add-Type -AssemblyName System.IO.Compression.FileSystem
$tempFolder = Join-Path -Path ([IO.Path]::GetTempPath()) -ChildPath (New-GUID).Guid
$compressedfiles = Get-ChildItem -Path 'C:\Intel' -Include '*.zip','*.CAB' -File -Recurse
$MatchedSourceFiles = foreach ($file in $compressedfiles) {
switch ($file.Extension) {
'.zip' {
# the destination folder should NOT already exist here
$null = [System.IO.Compression.ZipFile]::ExtractToDirectory($file.FullName, $tempFolder)
Get-ChildItem -Path $tempFolder -Filter '*.vip' -File -Recurse | ForEach-Object {
$null = [System.IO.Compression.ZipFile]::ExtractToDirectory($_.FullName, $tempFolder)
}
}
'.cab' {
# the destination folder MUST exist for expanding .cab files
$null = New-Item -Path $tempFolder -ItemType Directory -Force
expand.exe $file.FullName -F:* $tempFolder > $null
}
}
# now see if there are files with duplicate names
Get-ChildItem -Path $tempFolder -File -Recurse | Group-Object Name |
Where-Object { $_.Count -gt 1 } | ForEach-Object {
foreach ($item in $_.Group) {
# output objects to be collected in $MatchedSourceFiles
[PsCustomObject]#{
SourceArchive = $file.FullName
DuplicateFile = '.{0}' -f $item.FullName.Substring($tempFolder.Length) # relative path
}
}
}
# delete the temporary folder
$tempFolder | Remove-Item -Force -Recurse
}
# display on screen
$MatchedSourceFiles
# save as CSV file
$MatchedSourceFiles | Export-Csv -Path 'X:\DuplicateFiles.csv' -UseCulture -NoTypeInformation
The output would be something like this:
SourceArchive DuplicateFile
------------- -------------
D:\Test\test.cab .\test\CA2P30.BA0
D:\Test\test.cab .\test\dupes\CA2P30.BA0
D:\Test\test.zip .\test\CA2P3K.DAT
D:\Test\test.zip .\test\dupes\CA2P3K.DAT
D:\Test\test.zip .\test\CA2P60.BA0
D:\Test\test.zip .\test\dupes\CA2P60.BA0

Trying to use powershell to put files in folders based on their extension and the name of the folder

I have a directory with three files: .xlsx, .docx, and .txt, I also have folders in that same directory called xlsx, docx and txt. Basically trying to put each file into its corresponding folder, as a way to practice my PowerShell skills. I'm very new to PowerShell and have tried the following. I can tell its wrong, but I'm not quite sure why.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
foreach ($folder in $folders) {
if ("*$file.extension*" -like "*$folder.Name*") {
move-item $file -Destination "C:\Users\userA\Desktop\$folder.name"
}
}
}
Try the code below. With the Where-Object function, you find the corresponding file. I remove the dot because it is included in the extension otherwise.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
$folder = $folders | Where-Object { $_.Name -Like $file.Extension.Replace(".","") }
Move-Item -Path $file -Destination $folder
}
In your example, be careful how your strings are actually been interpreted. If you have "*$item.Name*" the string actually "* variable.Name*". In this case you need to use "*$($var.Name)*" in order to get the correct string.
Here are some adjustments to your approach that make it work. Breaking the -Destination parameter out to a separate variable $newpath lets you set a debug statement there so you can easily examine what it's creating.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
foreach ($folder in $folders) {
if ($file.extension.trim(".") -like $folder.Name) {
$newpath = ("{0}\{1}" -f $folder.FullName, $file.Name)
move-item $file -Destination $newpath
}
}
}
You could even create target folders for extensions if they do not exist yet:
$SourceFolder = C:\sample
$TargetFolder = D:\sample
Get-ChildItem -Path $SourceFolder -File |
ForEach-Object{
$DesinationFolder = Join-Path -Path $TargetFolder -ChildPath $_.Extension.TrimStart('.')
if(-not (Test-Path -Path $DesinationFolder)){
New-Item -Path $DesinationFolder -ItemType Directory | Out-Null
}
Copy-Item -Path $_.FullName -Destination $DesinationFolder -Force
}

Add multiple folders in one zip file in Powershell

Perhaps my question can be a duplicate, but I'm new in powershell, and cannot figure out, what is wrong with my script, that zipping particular directories:
$path = "C:\backup\DEV82"
if(!(Test-Path -Path $path )){
New-Item -ItemType directory -Path $path
}
cd C:\inetpub\wwwroot\dev82\
$SOURCE = Get-ChildItem * -Directory|Where-Object {$_.FullName -match "App_Config|Resources|bin"}
$dtstamp = (Get-Date).ToString("yyyyMMdd_HHmmss")
Add-Type -assembly "system.io.compression.filesystem"
Foreach ($s in $SOURCE)
{
$DESTINATION = Join-path -path $path -ChildPath "$dtstamp.zip"
If(Test-path $DESTINATION) {
Remove-item $DESTINATION
}
[io.compression.zipfile]::CreateFromDirectory($s.fullname, $DESTINATION)
}
If I execute command in $SOURCE variable, it gathers all required directories, which I want zip http://prntscr.com/j0sqri
$DESTINATION also returns valid value
PS C:\> $DESTINATION
C:\backup\DEV82\20180404_223153.zip
but right now only last folder (Resources) exists in zip file.
Ok, I rewrite my script, using, instead of Zipfile class, Compress-Archive with -Update ( -Update allows to add files\folders into existing archive )
$path = "C:\backup\DEV82"
if(!(Test-Path -Path $path )){
New-Item -ItemType directory -Path $path
}
cd C:\inetpub\wwwroot\dev82\
$SOURCE = Get-ChildItem * -Directory|Where-Object {$_.FullName -match "App_Config|Resources|bin"}
$dtstamp = (Get-Date).ToString("yyyyMMdd_HHmmss")
$DESTINATION = Join-path -path $path -ChildPath "$dtstamp.zip"
Add-Type -assembly "system.io.compression.filesystem"
If(Test-path $DESTINATION) {
Remove-item $DESTINATION
}
Foreach ($s in $SOURCE)
{
Compress-Archive -Path $s.fullname -DestinationPath $DESTINATION -Update
}
$SOURCE is already just a list of folder names, so you don't need the FullName property here:
[io.compression.zipfile]::CreateFromDirectory($s.fullname, $DESTINATION)
Either remove it, or remove the Select-Object from the pipeline here:
$SOURCE = Get-ChildItem * -Directory |
Where-Object {$_.FullName -match "App_Config|Resources|bin"} |
Select-Object -ExpandProperty FullName

Duplicate file name as folder, insert file

I am trying to use Powershell to
scan folder D://Mediafolder for names of media files
create a folder for each media file scanned, with same name
insert each media file in to matching folder name.
I can find no documentation or thread of this, and I am more fluent in Linux than Windows. I've tried many times to piece this together, but to no avail.
Hope this will help :)
This will create a folder for each file with the same name, so if you have a file called xyz.txt, it will create a folder called xyz and move the file to this folder.
$path = "D:\MediaFolder"
$items = Get-ChildItem $path
Foreach ($item in $items)
{
$folderName = $item.name.Split('.')[0]
New-Item "$path\$folderName" -ItemType Directory
Move-Item -Path "$path\$item" -Destination "$path\$foldername"
}
File Sorting based on extension should do the job:
$folder_path = read-host "Enter the folder path without space"
$file = gci $folder_path -Recurse | ? {-not $_.psiscontainer}
$file | group -property extension | % {if(!(test-path(join-path $folder_path -child $_.name.replace('.','')))){new-item -type directory $(join-path $folder_path -child $_.name.replace('.','')).toupper()}}
$file | % { move-item $_.fullname -destination $(join-path $folder_path -child $_.extension.replace(".",""))}
$a = Get-ChildItem $folder_path -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Remove-Item -Force
This will iterate over the files in the media_dir and move those with the extensions in media_types to a folder with the same basename. When you are satisfied that the files will be moved to the correct directory, remove the -WhatIf from the Move-Item statement.
PS C:\src\t> type .\ms.ps1
$media_dir = 'C:\src\t\media'
$new_dir = 'C:\src\t\newmedia'
$media_types = #('.mp3', '.mp4', '.jpeg')
Get-ChildItem -Path $media_dir |
ForEach-Object {
$base_name = $_.BaseName
if ($media_types -contains $_.Extension) {
if (-not (Test-Path $new_dir\$base_name)) {
New-Item -Path $new_dir\$base_name -ItemType Directory | Out-Null
}
Move-Item $_.FullName $new_dir\$base_name -WhatIf
}
}

Copy-Item and exclude folders

I need to copy all of my c:\inetpub directory to a new location but exclude the following folders and their subfolders:
c:\inetpub\custerr
c:\inetpub\history
c:\inetpub\logs
c:\inetpub\temp
c:\inetpub\wwwroot
So far I am doing this:
# Directory name is created with a format string
$dirName = "\\servername\folder1 _ {0}\inetpub" -f (get-date).ToString("yyyy-MM-dd-hh-mm-ss")
$dirName # Check the output
# Create dir if needed
if(-not (test-path $dirName)) {
md $dirName | out-null
} else {
write-host "$dirName already exists!"
}
#Copy Backup File to Dir
Copy-Item "\\servername\c$\inetpub\*" $dirName -recurse
This is a simple example of something you could do. Build an array of the parent folders that you want to exclude. Since you are accessing them via UNC paths we cannot really use the c:\ path (We can get around this but what I am about to show should be good enough.).
Then use Get-ChildItem to get all the folders in the inetpub directory. Filter out the exclusions using -notin and pass the rest to Copy-Item
$excludes = "custerr","history","logs","temp","wwwroot"
Get-ChildItem "c:\temp\test" -Directory |
Where-Object{$_.Name -notin $excludes} |
Copy-Item -Destination $dirName -Recurse -Force
You need at least PowerShell 3.0 for this to work.
Copy-Item -Path (Get-Item -Path "$path\*" -Exclude ('Folder1', 'File.cmd', 'File.exe', 'Folder2')).FullName -Destination $destination -Recurse -Force
Replace:
$path by your source folder
('Folder1', 'File.cmd', 'File.exe', 'Folder2') by your specific files/folder to exclude
$destination by your destination folder
Oh, the answer was SO simple, but it seems we are all PowerShell noobs.
New-Item -ItemType Directory -Force -Path $outDir # directory must exist
Copy-Item $inDir\* $outDir -Exclude #("node_modules",".yarn") -Recurse
It's the \* that makes it work.
PowerShell is awesome, but...
I wrote this for daily use and packaged it in the script module, it maintains all the directory structure and supports wildcards:
function Copy-Folder {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[String]$FromPath,
[Parameter(Mandatory)]
[String]$ToPath,
[string[]] $Exclude
)
if (Test-Path $FromPath -PathType Container) {
New-Item $ToPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
Get-ChildItem $FromPath -Force | ForEach-Object {
# avoid the nested pipeline variable
$item = $_
$target_path = Join-Path $ToPath $item.Name
if (($Exclude | ForEach-Object { $item.Name -like $_ }) -notcontains $true) {
if (Test-Path $target_path) { Remove-Item $target_path -Recurse -Force }
Copy-Item $item.FullName $target_path
Copy-Folder -FromPath $item.FullName $target_path $Exclude
}
}
}
}
Just call the Copy-Folder -FromPath inetpub -ToPath new-inetpub -Exclude custerr,history,logs,temp,wwwroot
The -FromPath and -ToPath can be omitted,
Copy-Folder inetpub new-inetpub -Exclude custerr,history,logs,temp,wwwroot
You can do something along the lines of:
?{$_.fullname -notmatch '\\old\\'}
after you get a hold of all your folders to filter them down.
This example would exclude anything containing "old" in the name. You can do this for directory you wish to exclude.
A full example:
C:\Example*" -include "*.txt -Recurse |
?{$_.fullname -notmatch '\\old\\'}|
% {Copy-Item $_.fullname "C:\Destination\"}
For multiple excludes you can use -And :
C:\Example*" -include "*.txt -Recurse |
?{$_.fullname -notmatch '\\old\\' -And $_.fullname -notmatch '\\old2\\'}|
% {Copy-Item $_.fullname "C:\Destination\"}