Last active
September 21, 2024 03:31
-
-
Save mobzystems/793007db28e3ffcc20e2 to your computer and use it in GitHub Desktop.
7-Zip commands for PowerShell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
Powershell Module 7-Zip - 7-Zip commands for PowerShell | |
The functions in this module call 7za.exe, the standAlone version of 7-zip to perform various tasks on 7-zip archives. | |
Place anywhere, together with 7za.exe and 7zsd.sfx. 7za.exe is required for all operations; 7zsd.sfx for creating self | |
extracting archives. | |
http://www.7-zip.org | |
Import-Module [Your path\]7-Zip | |
Brought to you by MOBZystems, Home of Tools - http://www.mobzystems.com/ | |
License: use at will! | |
#> | |
[CmdletBinding()] | |
Param() | |
# Be strict | |
Set-StrictMode -Version Latest | |
# Module variables | |
[string]$SCRIPT_DIRECTORY = Split-Path ($MyInvocation.MyCommand.Path) -Parent | |
[string]$7Z_SFX = Join-Path $SCRIPT_DIRECTORY "7zsd.sfx" | |
[string]$7Z_EXE = Join-Path $SCRIPT_DIRECTORY "7za.exe" | |
# Sanity checks | |
if (!(Test-Path -PathType Leaf $7Z_EXE)) { | |
Write-Warning "Cannot find 7za.exe in `"$SCRIPT_DIRECTORY`". This file is required for all operations in this module" | |
} | |
if (!(Test-Path -PathType Leaf $7Z_SFX)) { | |
Write-Warning "Cannot find 7zsd.sfx in `"$SCRIPT_DIRECTORY`". This file is required for New-7zSfx" | |
} | |
<# | |
This (internal) function does the hard work: it calls 7za with the appropriate arguments | |
#> | |
Function Perform7zOperation { | |
[CmdletBinding()] | |
Param( | |
# The operation to perform | |
[Parameter(Mandatory=$true)] | |
[ValidateSet("Add", "Update", "List", "Extract", "Test")] | |
[string]$Operation, | |
# The path of the archive | |
[Parameter(Mandatory=$true)] | |
[string]$Path, | |
# A list of file names or patterns to include | |
[Parameter(Mandatory=$true)] | |
[AllowEmptyCollection()] | |
[string[]]$Include, | |
# A list of file names or patterns to exclude | |
[Parameter(Mandatory=$true)] | |
[AllowEmptyCollection()] | |
[string[]]$Exclude, | |
# Apply include patterns recursively | |
[Parameter(Mandatory=$true)] | |
[switch]$Recurse, | |
# Additional switches for 7za | |
[Parameter(Mandatory=$true)] | |
[AllowEmptyString()] | |
[string]$Switches, | |
# Throw if the output does not contain "Everything is OK" | |
[Parameter(Mandatory=$false)] | |
[switch]$CheckOK = $true | |
) | |
switch ($Operation) { | |
"Add" { | |
$7zcmd = "a" | |
$verb = "Adding to" | |
} | |
"Update" { | |
$7zcmd = "u" | |
$verb = "Updating" | |
} | |
"Extract" { | |
$7zcmd = "e" | |
$verb = "Extracting" | |
} | |
"List" { | |
$7zcmd = "l" | |
$verb = "Listing" | |
} | |
"Test" { | |
$7zcmd = "t" | |
$verb = "Testing" | |
} | |
} | |
# Create a list of quoted file names from the $Include argument | |
[string]$files = "" | |
$Include | ForEach-Object { $files += " `"$_`"" } | |
$files = $files.TrimStart() | |
# Set up switches to use | |
$Switches += " -bd -y" # -bd: no percentage indicator, -y: Yes to all prompts | |
if ($Recurse) { | |
$Switches += " -r" # -r: recurse | |
} | |
# Add excludes to the switches | |
$Exclude | ForEach-Object { $Switches += " `"-x!$_`"" } | |
$Switches = $Switches.TrimStart() | |
Write-Verbose "$verb archive `"$Path`"" | |
[string]$cmd = "`"$7Z_EXE`" $7zcmd $Switches `"$Path`" $files" | |
Write-Debug $cmd | |
Invoke-Expression "&$cmd" -OutVariable output | Write-Verbose | |
# Check result | |
if ($CheckOK) { | |
if (-not ([string]$output).Contains("Everything is Ok")) { | |
throw "$verb archive `"$Path`" failed: $output" | |
} | |
} | |
# No error: return the 7-Zip output | |
Write-Output $output | |
} | |
<# | |
.SYNOPSIS | |
Create a new 7-Zip archive | |
.DESCRIPTION | |
Use this cmdlet to create 7-Zip archives. Possible types are 7z (default) and zip. | |
The archive file is overwritten if it exists! | |
.EXAMPLE | |
New-7zArchive new-archive *.txt | |
Creates a new 7-zip-archive named 'new-archive.7z' containing all files with a .txt extension | |
in the current directory | |
.EXAMPLE | |
New-7zArchive new-archive *.txt -Type zip | |
Creates a new zip-archive named 'new-archive.zip' containing all files with a .txt extension | |
in the current directory | |
.EXAMPLE | |
New-7zArchive new-archive *.jpg,*.gif,*.png,*.bmp -Recurse -Exclude tmp/ | |
Creates a new 7-zip archive named 'new-archive.7z' containing all files with an extension | |
of jpg, gif, png or bmp in the current directory and all directories below it | |
All files in the folder tmp are excluded, i.e. not included in the archive. | |
#> | |
Function New-7zArchive { | |
[CmdletBinding()] | |
Param( | |
# The path of the archive to create | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# A list of file names or patterns to include | |
[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] | |
[string[]]$Include, | |
# A list of file names or patterns to exclude | |
[Parameter(Mandatory=$false)] | |
[string[]]$Exclude = @(), | |
# The type of archive to create | |
[ValidateSet("7z", "zip")] | |
[string]$Type = "7z", | |
# Apply include patterns recursively | |
[switch]$Recurse, | |
# Additional switches for 7za | |
[string]$Switches = "" | |
) | |
Begin { | |
# Make sure the archive is deleted before it is created | |
if (Test-Path -PathType Leaf $Path) { | |
Remove-Item $Path | Out-Null | |
} | |
$filesToProcess = @() | |
} | |
Process { | |
$filesToProcess += $Include | |
} | |
End { | |
$Switches = "$Switches -t$Type" | |
[string[]]$result = Perform7zOperation -Operation Add -Path $Path -Include $filesToProcess -Exclude $Exclude -Recurse:$Recurse -Switches $Switches | |
} | |
} | |
<# | |
.SYNOPSIS | |
Add files to a 7-Zip archive | |
.DESCRIPTION | |
Use this cmdlet to add files to an existing 7-Zip archive. If the archive does not | |
exists, it is created | |
#> | |
Function Add-7zArchive { | |
[CmdletBinding()] | |
Param( | |
# The path of the archive to add to | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# A list of file names or patterns to include | |
[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] | |
[string[]]$Include, | |
# A list of file names or patterns to exclude | |
[Parameter(Mandatory=$false)] | |
[string[]]$Exclude = @(), | |
# The type of archive to create | |
# Apply include patterns recursively | |
[switch]$Recurse, | |
# Additional switches for 7za | |
[string]$Switches = "" | |
) | |
Begin { | |
$filesToProcess = @() | |
} | |
Process { | |
$filesToProcess += $Include | |
} | |
End { | |
[string[]]$result = Perform7zOperation -Operation Add -Path $Path -Include $filesToProcess -Exclude $Exclude -Recurse:$Recurse -Switches $Switches | |
} | |
} | |
<# | |
.SYNOPSIS | |
Update files in a 7-Zip archive | |
.DESCRIPTION | |
Use this cmdlet to update files to an existing 7-Zip archive. If the archive does not | |
exists, it is created | |
#> | |
Function Update-7zArchive { | |
[CmdletBinding()] | |
Param( | |
# The path of the archive to update | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# A list of file names or patterns to include | |
[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] | |
[string[]]$Include, | |
# A list of file names or patterns to exclude | |
[Parameter(Mandatory=$false)] | |
[string[]]$Exclude = @(), | |
# Apply include patterns recursively | |
[switch]$Recurse, | |
# Additional switches for 7za | |
[string]$Switches = "" | |
) | |
Begin { | |
$filesToProcess = @() | |
} | |
Process { | |
$filesToProcess += $Include | |
} | |
End { | |
[string[]]$result = Perform7zOperation -Operation Update -Path $Path -Include $filesToProcess -Exclude $Exclude -Recurse:$Recurse -Switches $Switches | |
} | |
} | |
<# | |
.SYNOPSIS | |
Extract files fom a 7-Zip archive | |
.DESCRIPTION | |
Use this cmdlet to extract files from an existing 7-Zip archive | |
.EXAMPLE | |
Expand-7zArchive backups.7z | |
#> | |
Function Expand-7zArchive { | |
[CmdletBinding()] | |
Param( | |
# The path of the archive to update | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# The path to extract files to | |
[Parameter(Mandatory=$false, Position=1)] | |
[string]$Destination = ".", | |
# A list of file names or patterns to include | |
[Parameter(Mandatory=$false, ValueFromPipeLine=$true, Position=2)] | |
[string[]]$Include = @("*"), | |
# A list of file names or patterns to exclude | |
[Parameter(Mandatory=$false)] | |
[string[]]$Exclude = @(), | |
# Apply include patterns recursively | |
[switch]$Recurse, | |
# Additional switches for 7za | |
[string]$Switches = "", | |
# Force overwriting existing files | |
[switch]$Force | |
) | |
Begin { | |
$Switches = $Switches + " `"-o$Destination`"" | |
if ($Force) { | |
$Switches = $Switches + " -aoa" # Overwrite ALL | |
} else { | |
$Switches = $Switches + " -aos" # SKIP extracting existing files | |
} | |
$filesToProcess = @() | |
} | |
Process { | |
$filesToProcess += $Include | |
} | |
End { | |
[string[]]$result = Perform7zOperation -Operation Extract -Path $Path -Include $filesToProcess -Exclude $Exclude -Recurse:$Recurse -Switches $Switches | |
$result | ForEach-Object { | |
if ($_.StartsWith("Skipping ")) { | |
Write-Warning $_ | |
} | |
} | |
} | |
} | |
<# | |
.SYNOPSIS | |
List the files in a 7-Zip archive. | |
.DESCRIPTION | |
Use this cmdlet to examine the contents of 7-Zip archives. | |
Output is a list of PSCustomObjects with properties [string]Mode, [DateTime]DateTime, [int]Length, [int]Compressed and [string]Name | |
.EXAMPLE | |
Get-7zArchive c:\temp\test.7z | |
List the contents of the archive "c:\temp\test.7z" | |
#> | |
Function Get-7zArchive { | |
[CmdletBinding()] | |
[OutputType([PSCustomObject[]])] | |
Param( | |
# The name of the archive to list | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# Additional switches | |
[Parameter(Mandatory=$false)] | |
[string]$Switches = "" | |
) | |
[string[]]$result = Perform7zOperation -Operation List -Path $Path -Include @() -Exclude @() -Recurse:$false -Switches $Switches -CheckOK:$false | |
[bool]$separatorFound = $false | |
[int]$filecount = 0 | |
$result | ForEach-Object { | |
if ($_.StartsWith("------------------- ----- ------------ ------------")) { | |
if ($separatorFound) { | |
# Second separator! We're done | |
break | |
} | |
$separatorFound = -not $separatorFound | |
} else { | |
if ($separatorFound) { | |
# 012345678901234567890123456789012345678901234567890123456789012345678901234567890 | |
# x-----------------x x---x x----------x x----------x x-------------------- | |
# 2015-12-20 14:25:18 ....A 18144 2107 XMLClassGenerator.ini | |
[string]$mode = $_.Substring(20, 5) | |
[DateTime]$datetime = [DateTime]::ParseExact($_.Substring(0, 19), "yyyy'-'MM'-'dd HH':'mm':'ss", [CultureInfo]::InvariantCulture) | |
[int]$length = [int]"0$($_.Substring(26, 12).Trim())" | |
[int]$compressedlength = [int]"0$($_.Substring(39, 12).Trim())" | |
[string]$name = $_.Substring(53).TrimEnd() | |
# Write a PSCustomObject with properties to output | |
Write-Output ([PSCustomObject] @{ | |
Mode = $mode | |
DateTime = $datetime | |
Length = $length | |
Compressed = $compressedlength | |
Name = $name | |
}) | |
$filecount++ | |
} | |
} | |
} | |
} | |
<# | |
.SYNOPSIS | |
Test a new 7-Zip archive. | |
.DESCRIPTION | |
Use this cmdlet to test 7-Zip archives for errors | |
.EXAMPLE | |
Test-7zArchive c:\temp\test.7z | |
Test the archive "c:\temp\test.7z". Throw an error if any errors are found | |
#> | |
Function Test-7zArchive { | |
[CmdletBinding()] | |
Param( | |
# The name of the archive to test | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# Additional switches | |
[Parameter(Mandatory=$false, Position=1)] | |
[String]$Switches = "" | |
) | |
[string[]]$result = Perform7zOperation -Operation Test -Path $Path -Include @() -Exclude @() -Recurse:$false -Switches $Switches -CheckOK:$false | |
# Check result | |
if ($result.Contains("No files to process")) { | |
Write-Verbose "Archive is empty" | |
return | |
} | |
if ($result.Contains("cannot find archive")) { | |
throw "Archive `"$Path`" not found" | |
} | |
if ($result.Contains("Everything is Ok")) { | |
Write-Verbose "Archive is OK" | |
return | |
} | |
# In all other cases, we have an error. Write out the results Verbose | |
$result | Write-Verbose | |
# ... and throw an error | |
throw "Testing archive `"$Path`" failed: $result" | |
} | |
<# | |
.SYNOPSIS | |
Create a new 7-Zip self extracting archive | |
.DESCRIPTION | |
Create self-extracting archives using 7-Zip | |
.EXAMPLE | |
New-7zsfx app-sfx app.exe,app.exe.config app.exe | |
Simply create a self-extracting exe from an executable file app.exe | |
with its configuration file app.exe.config: | |
#> | |
Function New-7zSfx { | |
[CmdletBinding()] | |
Param( | |
# The name of the exe-file to produce, without extension | |
[Parameter(Mandatory=$true, Position=0)] | |
[string]$Path, | |
# The files to include in the archive | |
[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)] | |
[string[]]$Include, | |
# The command to run when the sfx archive is started | |
[Parameter(Mandatory=$true, Position=2)] | |
[string]$CommandToRun, | |
# Title for messages | |
[Parameter(Mandatory=$false)] | |
[string]$Title, | |
# Begin Prompt message | |
[Parameter(Mandatory=$false)] | |
[string]$BeginPrompt, | |
# Title of extraction dialog | |
[Parameter(Mandatory=$false)] | |
[string]$ExtractTitle, | |
# Text in dialog | |
[Parameter(Mandatory=$false)] | |
[string]$ExtractDialogText, | |
# Button text of cancel button | |
[Parameter(Mandatory=$false)] | |
[string]$ExtractCancelText, | |
# A list of additional options, of the form "key=value" | |
[Parameter(Mandatory=$false)] | |
[string[]]$ConfigOptions, | |
# Include subdirectories | |
[switch]$Recurse, | |
# Additional switches to pass to 7za when creating the archive | |
[string]$Switches = '' | |
) | |
Begin { | |
# Get the base name of the specified path in Name | |
if (-not [IO.Path]::IsPathRooted($Path)) { | |
$Path = Join-Path "." $Path | |
} | |
# The join the directory name with the file name exluding the extension | |
[string]$Name = Join-Path ([IO.Path]::GetDirectoryName($Path)) ([IO.Path]::GetFileNameWithoutExtension($Path)) | |
[string]$tmpfile = "$Name.sfx.tmp" | |
[string]$exefile = "$Name.exe" | |
if (Test-Path -PathType Leaf "$exefile") { | |
Remove-Item "$exefile" -Force | |
} | |
$filesToInclude = @() | |
} | |
Process { | |
$filesToInclude += $Include | |
} | |
End { | |
# Escape a variable for the config file | |
Function Escape([string]$t) { | |
# Prefix \ and " with \, replace CRLF with \n and TAB with \t | |
Return $t.Replace('\', '\\').Replace('"', '\"').Replace("`r`n", '\n').Replace("`t", '\t') | |
} | |
[string[]]$output = New-7zArchive -Path $tmpfile -Include $filesToInclude -Exclude @() -Type 7z -Recurse:$Recurse -Switches $Switches | |
# Copy sfx + archive + config to exe | |
<# | |
http://www.7zsfx.info/en/ | |
Title - title for messages | |
BeginPrompt - Begin Prompt message | |
Progress - Value can be "yes" or "no". Default value is "yes". | |
RunProgram - Command for executing. Default value is "setup.exe". Substring %%T will be replaced with path to temporary folder, where files were extracted | |
Directory - Directory prefix for "RunProgram". Default value is ".\\" | |
ExecuteFile - Name of file for executing | |
ExecuteParameters - Parameters for "ExecuteFile" | |
ExtractTitle - title of extraction dialog | |
ExtractDialogText - text in dialog | |
ExtractCancelText - button text of cancel button | |
#> | |
[string]$cfg = @" | |
;!@Install@!UTF-8! | |
Title="$Title" | |
RunProgram="$(Escape($CommandToRun))" | |
"@ | |
if ($BeginPrompt -ne "") { | |
$cfg += "BeginPrompt=`"$(Escape($BeginPrompt))`"`r`n" | |
} | |
if ($ExtractTitle -ne "") { | |
$cfg += "ExtractTitle=`"$(Escape($ExtractTitle))`"`r`n" | |
} | |
if ($ExtractDialogText -ne "") { | |
$cfg += "ExtractDialogText=`"$(Escape($ExtractDialogText))`"`r`n" | |
} | |
if ($ExtractCancelText -ne "") { | |
$cfg += "ExtractCancelText=`"$(Escape($ExtractCancelText))`"`r`n" | |
} | |
if ($ConfigOptions -ne $null) { | |
$ConfigOptions | ForEach-Object { | |
[string[]]$parts = $_.Split('=') | |
if ($parts.Length -lt 2) { | |
throw "Invalid configuration option '$($_)': missing '='" | |
} else { | |
$cfg += "$($parts[0])=`"$(Escape($parts[1]))`"`r`n" | |
} | |
} | |
} | |
$cfg += ";!@InstallEnd@!`r`n" | |
Write-Verbose "Creating sfx `"$exefile`"..." | |
Write-Debug $cfg | |
[string]$cfgfile = "$Name.sfx.cfg" | |
Set-Content "$cfgfile" -Value $cfg | |
Get-Content "$7Z_SFX","$cfgfile","$tmpfile" -Encoding Byte -Raw | Set-Content "$exefile" -Encoding Byte | |
Remove-Item "$tmpfile" | |
Remove-Item "$cfgfile" | |
} | |
} | |
Export-ModuleMember -Function *-7z* |
for some reason the break keyword at line 372 also break outside loops. So when I loop on multiple archives, only the first one is processed. As as very dirty fix I replace the foreach-object with a foreach($_ in $result) .
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is one very useful module and thanks a lot for sharing. We had one minor issue, since we are dealing with (very) large files. We had to change the int specified for file length at line 382 to long to make Get-7zipArchive stop barking at us.