How to create several zip folders (function) from my output? - powershell

I am trying to zip all folders I find in my folder called services.
I use Get-Childitem to find these folders and I want to add the function after the pipeline, but it doesn't work out the way I want.
The zip file should have the same name as the folder itself, so I tried to give the name with "$.FullName" and destinationpath is the folder "C:\com\$.Name"
Here is my script :
Get-ChildItem "C:\com\services" | % $_.FullName
$folder = "C:\com\services"
$destinationFilePath = "C:\com"
function create-7zip([String] $folder, [String] $destinationFilePath)
{
[string]$pathToZipExe = "C:\Program Files (x86)\7-Zip\7zG.exe";
[Array]$arguments = "a", "-tzip", "$destinationFilePath", "$folder";
& $pathToZipExe $arguments;
}

First. Declare variables like folder and destination path.
Second. Change your 7zip folderpath as mine is in (Program Files).
#declare variables
$folder = "C:\com\services"
$destPath = "C:\destinationfolder\"
#Define the function
function create-7zip{
param([String] $folder,
[String] $destinationFilePath)
write-host $folder $destinationFilePath
[string]$pathToZipExe = "C:\Program Files\7-Zip\7z.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)
}
$_.PSIsContainer will find only the folders, constructing destination path variable $dest and then calling the function. I hope this helps.

If I understand you correctly, you want to pipe the output of gci into your Create-7Zip function, and have the function create a zip file named after each directory you're passing in, like this:
gci | ?{ $_.PSIsContainer } | Create-7Zip
To do this you'll need the cmdlet you're writing to support taking values from the pipeline, which you do with a [Parameter] attribute in your list of params().
function Create-7Zip
{
param(
[Parameter(ValueFromPipeline=$True)]
[IO.DirectoryInfo]$Directory #we're accepting directories from the pipeline. Based on the directory we'll get the zip name
);
BEGIN
{
$7Zip = Join-Path $env:ProgramFiles "7-Zip\7z.exe"; #get executable
}
PROCESS
{
$zipName = $("{0}.zip" -f $Directory.Name);
$7zArgs = Write-Output "a" "-tzip" $zipName $directory.FullName; #Q&D way to get an array
&$7Zip $7zArgs
}
}
Usage:
#Powershell 3.0
get-childitem -directory | Create-7Zip
#Powershell 2
get-childitem | ?{ $_.PSIsContainer } | Create-7Zip
You'll see the output of 7zip; you can capture this information by piping it to somewhere else.

Related

How to unzip all files in folder? (Not .zip extension)

Currently, I am writing a script that moves PDF files that are wrongfully zipped to a certain folder. I have achieved that. The next thing I need to get to work, is that the zipped .pdf files get unzipped into a different folder.
This is my whole script. Everything except for the last 2 lines is dedicated to finding the PDF files that are zipped and moving them.
In the first parts, the script checks the first few bytes of every pdf file in the folder. If they start with "PK*", they are zip files and get moved to the zipped folder.
For every PDF/zip file, there is one associated HL7 file in the folder next to it.
These also need to get moved to the same folder. From there the zip files need to be unzipped and relocated to "unzipped"
The last 2 lines are for unzipping.
$pdfDirectory = 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\struktur_id_1225\ext_dok'
$newLocation = 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\Zip'
Get-ChildItem "$pdfDirectory" -Filter "*.pdf" | foreach {
if ((Get-Content $_.FullName | select -First 1 ) -like "PK*") {
$HL7 = $_.FullName.Replace("ext_dok","MDM")
$HL7 = $HL7.Replace(".pdf",".hl7")
move $_.FullName $newLocation;
move $HL7 $newLocation
}
}
Get-ChildItem 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\Zip' |
Expand-Archive -DestinationPath 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\Zip\unzipped' -Force
This, sadly, doesn't work.
I suspect that it's because these files dont have the .zip extension. The only Filter that works for Expand-Archive is .zip.
So I need to find a way to get this function to unzip the files, even though they dont have the fitting extension...
Like #Ansgar said this would be the way to go:
Param (
$SourcePath = 'C:\Users\xxx\Downloads\PDF',
$ZipFilesPath = 'C:\Users\xxx\Downloads\ZIP',
$UnzippedFilesPath = 'C:\Users\xxx\Downloads\Unzipped'
)
$VerbosePreference = 'Continue'
#region Test folders
#($SourcePath, $ZipFilesPath, $UnzippedFilesPath) | Where-Object {
-not (Test-Path -LiteralPath $_)
} | ForEach-Object {
throw "Path '$_' not found. Make sure that the folders exist before running the script."
}
#endregion
#region Get all files with extension .pdf
$Params = #{
Path = Join-Path -Path $SourcePath -ChildPath 'ext_dok'
Filter = '*.pdf'
}
$PDFfiles = Get-ChildItem #Params
Write-Verbose "Got $($PDFfiles.count) files with extension '.pdf' from '$($Params.Path)'"
#endregion
#region Move PDF and HL7 files
$MDMpath = Join-Path -Path $SourcePath -ChildPath 'MDM'
foreach ($PDFfile in ($PDFfiles | Where-Object {
(Get-Content $_.FullName | Select-Object -First 1) -like 'PK*'})
) {
$MoveParams = #{
Path = $PDFfile.FullName
Destination = Join-Path -Path $ZipFilesPath -ChildPath ($PDFfile.BaseName + '.zip')
}
Move-Item #MoveParams
Write-Verbose "Moved file '$($MoveParams.Path)' to '$($MoveParams.Destination)'"
$GetParams = #{
Path = Join-Path -Path $MDMpath -ChildPath ($PDFfile.BaseName + '.hl7')
ErrorAction = 'Ignore'
}
if ($HL7file = Get-Item #GetParams) {
$MoveParams = #{
Path = $HL7file
Destination = $ZipFilesPath
}
Move-Item #MoveParams
Write-Verbose "Moved file '$($MoveParams.Path)' to '$($MoveParams.Destination)$($HL7file.Name)'"
}
}
#endregion
#region Unzip files
$ZipFiles = Get-ChildItem -Path $ZipFilesPath -Filter '*.zip' -File
foreach ($ZipFile in $ZipFiles) {
$ZipFile | Expand-Archive -DestinationPath $UnzippedFilesPath -Force
Write-Verbose "Unzipped file '$($ZipFile.Name)' in folder '$UnzippedFilesPath'"
}
#endregion
Some tips:
Add a Param () clause at the beginning of the script to contain all your variables that can change.
Try to use the full parameter name to clearly indicate what is what. Use Get-ChildItem -Path xxx instead of Get-ChildItem xxx.
Use hash tables for long parameters. This makes the code more compact in width and more easily to read.
Use #region and #endregion to group your code.

Is there a way to set a path using regular expression?

I'm using PowerShell command prompt and I want to set a location to the folder "Folder". The thing is that folder can be everywhere.
I've tried the command Set-Location and Resolve-Path but I don't get it.
I want to do something like that:
$path = Resolve-Path ".*/Folder"
Set-Location $path
Where the .* can be all the parent folder
Any idea?
Would try this:
Get-ChildItem -Path .\ -Name Folder -Recurse -Depth 10
Hope it helps. BR
Edit (see comments):
$array = Get-ChildItem -Path .\ -Name Ping -Recurse -Depth 10
if($array.Count -eq 0){
#stay
}
if($array.Count -eq 1){
Set-Location $array
}
else{
$array | ForEach-Object{Write-Host $_}
}
Requires PowerShell v3 or higher, you can check using $PSVersionTable.*
$path = (Get-ChildItem . -Recurse -Directory | Where-Object { $_.Name -eq "Folder" }).FullName
This will give you all the directories named Folder in current directory and its subdirectories recursively.
The query, however, can return multiple results so if you want to choose the first one use [0]. Also, to cover the case when the query returns no results, enforce returned object to be an array using #( ... ) and check if it exists:
$f = #(Get-ChildItem . -Recurse -Directory | Where-Object { $_.Name -eq "Folder" })[0].FullName
if ( $f ) {
cd $f
}
else {
# Handle the error
}
cd is alias to Set-Location so you can use whichever you prefer. Important thing to remember is to use FullName property as it contains full path to the folder.
* For PowerShell versions lower than v3, use | Where-Object {$_.PSIsContainer -eq $true} instead of -Directory

Compress Files from list with PowerShell 5

I have a file with a list of files with directions.
Like this:
XXXXXX/sample.txt
XXXXXX/dog.txt
XXXXXX/cat.docx
ZZZZ/lamp.jpg
How can I to compress all files and save files with sub-directions.
Now, I can to compress all files but without directions.
Like this:
sample.txt
dog.txt
cat.docx
lamp.jpg
Sorry on my bad English.
foreach ($filename in Get-Content .\ListOfFilesToZip.txt)
{
Compress-Archive -Update $filename .\ZipFile.zip
}
The following function can compresses a whole folder structure into a single zip file and will keep the structure within the zip (even works with PowerShell <5).
function createZipFile($outputFileName, $sourceDirectory){
Add-Type -AssemblyName System.IO.Compression.FileSystem
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
[System.IO.Compression.ZipFile]::CreateFromDirectory($sourceDirectory, $outputFileName, $compressionLevel, $false)
}
Call it like that:
createZipFile "c:\temp\output.zip" "c:\folder\to\compress"
Not sure if there's any better way of doing this, but you can create a temp folder, copy the files to be archived along with desired folder structure over there and then just zip up the whole thing.
Take a look here (not my code, but seems to do exactly that) for a sample
Edit If question is about getting filename from the path
The most efficient way would be with a regex, but they can be rather complicated if you are not used to working with them.
A simpler way would be to simply split each string and select the last part where the file name is:
$string = "c:\asd\qwe\zxc\dog.txt"
#Split the string on each \
$string.Split("\")
#This will output a list like this
c:
asd
qwe
zxc
dog.txt
Now we want to simply select the last entry in this list as the filename is always last in a path. So we use Select Object for that.
$string.Split("\") | Select-Object -Last 1
This will return:
dog.txt
You can run this through a foreach on your list to get it for each item.
function DirNewTemp {
$tmpdir = (New-TemporaryFile).FullName
Remove-Item -Force -Path $tmpdir -ErrorAction 'SilentlyContinue'
New-Item -Type Directory $tmpdir | out-null
return $tmpdir
}
function ZipFiles {
param(
[string]$Zip,
[string[]]$FileList
)
write-host ""
write-host "====================================="
write-host "ZipFile $Zip"
write-host "====================================="
$pwd = (Get-Location).Path
$tmpdir = DirNewTemp
try {
$count = $FileList.Count
foreach ($file in $FileList) {
$pwd_escaped = [Regex]::Escape($pwd)
$save_file = $file -replace "^${pwd_escaped}\\", "${tmpdir}\"
$save_path = Split-Path $save_file -Parent
New-Item -Force -Path $save_path -ItemType Directory | out-null
Copy-Item -Force $file $save_file
}
set-location $tmpdir
$dest = "$pwd\$Zip";
write-host "Creating Zip: $dest"
Compress-Archive -Force -Path * -DestinationPath $dest
}
finally {
write-host "cleanup"
set-location $pwd
remove-item -Force -Recurse -Path $tmpdir -ErrorAction 'SilentlyContinue'
}
}

Using Powershell to move documents to a folder based on file name

I have searched your database for answers and find things that are similar, but not quite what I am needing. I do not know how to code well enough to take some of the examples and modify them to fit my needs.
I have documents that I need to move to a folder that has the same name as the beginning of the document name. Here are a couple of example of my document names (ParcelNum_CountyNum_Year_DocType.pdf)
188777_014_2013_NOAV.pdf
242353_227_2014_HRGNOTICE.pdf
R506275_246_2013_NOAV.pdf
I currently have the documents residing in a single folder named by the county number (the 3 digit number between the first _ and the 2nd _)
I have folders created that are named by the ParcelNum, which is the first set of characters/numbers at the beginning and before the first _. I need Powershell to read the first portion of the document name stopping at the first _ and place it in the folder that matches that numchar.
So for example the first document example above is named 188777_014_2013_NOAV.pdf and resides at the root of the CountyNum folder named 014.
For that example, I would need it to be moved from the root folder 014, into its subfolder named 188777.
I hope that makes sense. Please feel free to ask questions.
Thank you,
Here's what I would do. This is under the assumption that the files you want to move are in a parent folder and the root & sub folders you want to sort them to are also in the same folder, i.e. if the example PDF's path is C:\test\Docs\188777_014_2013_NOAV.pdf and you want it to end up in C:\test\Docs\014\188777.
Update the path in the $basePath variable to your liking. Comments are included above each step with explanations. Enjoy!
# This is the parent directory for the files and sorted folders
$basePath = "C:\test\Docs"
# This sets that folder as your CWD (Current working directory)
Set-Location $basePath
# This grabs the *files* underneath the parent directory, ignoring sub directories
$files = Get-ChildItem $basePath | Where-Object {$_.PSIsContainer -eq $false}
# Starts iterating through each file
foreach ($file in $files) {
# Split the base file name by underscore (creates an array with each part of the name seperated)
$split = $file.BaseName -split "_"
# Store the second item [1] in the array $split as the root folder name
$root = "$basePath\$($split[1])"
# Store the first item [0] in the array $split as the sub folder name
$sub = "$root\$($split[0])"
# Check if the root folder exists, create it if not
if (!(Test-Path $root -ErrorAction SilentlyContinue)) {
New-Item $root -ItemType Directory | Out-Null
}
# Check if the sub folder exists, create it if not
if (!(Test-Path $sub -ErrorAction SilentlyContinue)) {
New-Item $sub -ItemType Directory | Out-Null
}
# Move the file to the sub folder
Move-Item $file.FullName -Destination $sub -Verbose
}
Tried to solve your problem:
#Requires -Version 4.0
function Move-FileToDirectoryWithSamePrefix
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[ValidateScript( {Test-Path $_ -PathType Container})]
[string]
$DirectoryToMoveFileTo,
[Parameter(Mandatory = $true, Position = 1)]
[ValidateScript( {Test-Path $_ -PathType Container})]
[string]
$DirectoryToSearchFiles
)
Begin {
# Needed to find all files for movement
$fileRegex = ".*_[0-9]{3}_[0-9]{4}_.*\.pdf"
$currentLocation = Get-Location
}
Process{
# find files to move
$matchingFile = Get-ChildItem -Path $DirectoryToSearchFiles -Recurse | Where-Object { $_.Name -match $fileRegex }
# Change to destination directory
Set-Location $DirectoryToMoveFileTo
foreach ($file in $matchingFile) {
$directoryName = ($file -split "_")[0]
# Find the director to move to
$dirToMoveFile = Get-ChildItem -Recurse -Include $directoryName
if ($dirToMoveFile -eq $null) {
# Create directory if not existing
$dirToMoveFile = New-Item -Name $directoryName -Path $DirectoryToMoveFileTo -ItemType Directory
}
Move-Item -Path $file.FullName -Destination $dirToMoveFile.fullName
}
}
End{
Set-Location $currentLocation
}
}
Store above in a ps1 file, e.g. moveFile.ps1. Afterwards open a PowerShell and source the file:
PS C:\> . C:\PathToYourPs1File\moveFile.ps1
Afterwards you can the function like:
PS C:\temp> Move-FileToDirectoryWithSamePrefix -DirectoryToMoveFileTo .\filesDestination -DirectoryToSearchFiles .\files
If you need to debug it open the ps1 in the Powershell ISE, set a breakpoint and start the script. For further information see this link.
Hope that helps.

Issue with archiving files

I am trying to archiving old files in the server (older then 90 days) and it should be separate ZIP file for every month. I have powershell-v1.0 so I am not able to use System.Management.Automation.PSObject
I have created a script but I have a problem with zip file name. When I run the script all files are moving to one archive with name +++.
$folders ="C:\New folder\test\"
Function Zip
{
Param
(
[string]$zipFile
,
[string[]]$toBeZipped
)
$CurDir = Get-Location
Set-Location "C:\program files\7-zip\"
.\7z.exe A -tzip $zipFile $toBeZipped | Out-Null
Set-Location $CurDir
}
$files = Get-ChildItem -Path $folders | Where-Object {$_.LastwriteTime -lt ((Get-date).adddays(-90))} | % { $_.FullName};
if ( !$files)
{break}
else
{
Write-Host $files
$file_year = $files.LastwriteTime.Year
$file_month = $files.LastwriteTime.Month
echo $file_month
ZIP $folders+"$file_year"+"$file_month"+".zip" $files
If(Test-Path $folders+$file_year+$file_month+.zip)
{
Remove-Item $files
}}
It would be nice if someone can figure out what am I doing wrong.
There are two issues why this doesn't work. First, you are selecting (using the ForEach-Object cmdlet %) the FullName property thus won't be able to access the LastWriteTime property anymore. Secondly, you are trying to access the property on a potential array of files (which year and month you want to use?)
So I would change / refactor your script to something like this (untested).
$folders ="C:\New folder\test\"
function Write-ZipFile
{
Param
(
[string]$Path,
[string[]]$Files
)
$7zip = Join-Path $env:ProgramFiles '\7-zip\7z.exe'
& $7zip A -tzip $zipFile $toBeZipped | Out-Null
}
$files = Get-ChildItem -Path $folders |
Where-Object { $_.LastWriteTime -lt ((Get-date).AddDays(-90)) }
$zipFile = Join-Path $folders ('{0}{1}.zip' -f $files[0].LastwriteTime.Year, $files[0].LastwriteTime.Month)
Write-ZipFile -Path $zipFile -Files ($files | select -ExpandProperty FullName)
The list of file names to archive is in $files. Use $files.GetType() to see that it is an array. Use $files[0].GetType() to see that each element is a string type, not a file object type.
$files = Get-ChildItem -Path $folders |
Where-Object {$_.LastwriteTime -lt (Get-date).adddays(-90)}
I imagine that you will want to omit directories.
The $files array will be an array of FileInfo objects, not strings.
Secondly, do something to iterate over the list of FileInfo objects.
[cmdletbinding()]
Param()
$folders = "H:\src\powershell\afm"
Function Zip
{
Param (
[string]$zipFile
, [string[]]$toBeZipped
)
& "C:\Program Files\7-Zip\7z.exe" A -tzip $zipFile $toBeZipped | Out-Null
}
$files = Get-ChildItem -Path $folders -File |
Where-Object {($_.LastwriteTime -lt (Get-date).adddays(-10)) -and ($_.Extension -ne ".zip")}
$files | ForEach-Object {
Write-Verbose "Working on file $_"
$file_year = $_.LastwriteTime.Year
$file_month = $_.LastwriteTime.Month
Write-Verbose "file_month is $file_month"
Zip "$folders\$file_year$file_month.zip" "$folders\$_"
If (Test-Path "$folders\$file_year$file_month.zip")
{
### Remove-Item $_q
}
}
It would appear that the problem is that there is nothing to process each file to archive them. There is no ForEach-Object.
There is no LastWriteTime on an array object, only on a FileInfo object.