Skip to content

Instantly share code, notes, and snippets.

@rgilmutdinov
Last active June 2, 2022 09:54
Show Gist options
  • Save rgilmutdinov/1286739c455744f27d3f69c8c435ce4e to your computer and use it in GitHub Desktop.
Save rgilmutdinov/1286739c455744f27d3f69c8c435ce4e to your computer and use it in GitHub Desktop.
ZipFileProvider for PowerShell
// Import module
Import-Module ./PSZip.dll
// Create new drive referring to .zip arvhive
New-PSDrive -Name "ZIP" -PSProvider "ZipFile" -Root "D:\files\archive.zip"
// List zip archive contents
Get-ChildItem -Path ZIP: -Recurse
// List only *.txt files
Get-ChildItem -Path ZIP: -Recurse -Filter *.txt
// ...or
dir ZIP: -Recurse *.txt
using System;
using System.Runtime.InteropServices;
namespace PSZip
{
[Flags]
public enum MatchPatternFlags : uint
{
Normal = 0x00000000, // PMSF_NORMAL
Multiple = 0x00000001, // PMSF_MULTIPLE
DontStripSpaces = 0x00010000 // PMSF_DONT_STRIP_SPACES
}
public class FileName
{
[DllImport("Shlwapi.dll", SetLastError = false)]
static extern int PathMatchSpecExW(
[MarshalAs(UnmanagedType.LPWStr)] string file,
[MarshalAs(UnmanagedType.LPWStr)] string spec,
MatchPatternFlags flags);
public static bool MatchPattern(string file, string spec, MatchPatternFlags flags)
{
if (string.IsNullOrEmpty(file))
{
return false;
}
if (string.IsNullOrEmpty(spec))
{
return true;
}
int result = PathMatchSpecExW(file, spec, flags);
return (result == 0);
}
}
}
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
namespace PSZip
{
public static class ZipArchiveExtensions
{
public static IEnumerable<ZipArchiveEntry> GetFiles(this ZipArchive archive, string filter = null)
{
return GetChildren(archive, "", false, filter);
}
public static IEnumerable<ZipArchiveEntry> GetDirectories(this ZipArchive archive)
{
return GetChildren(archive, "", true);
}
public static IEnumerable<ZipArchiveEntry> GetFiles(this ZipArchiveEntry entry, string filter = null)
{
return GetChildren(entry, false, filter);
}
public static IEnumerable<ZipArchiveEntry> GetDirectories(this ZipArchiveEntry entry)
{
return GetChildren(entry, true);
}
public static string GetName(this ZipArchiveEntry entry)
{
if (IsDirectory(entry))
{
string[] parts = entry.FullName.Split(ZipFileProvider.PathSeparators, StringSplitOptions.RemoveEmptyEntries);
return $"{parts.Last()}/";
}
return entry.Name;
}
private static IEnumerable<ZipArchiveEntry> GetChildren(ZipArchiveEntry parentEntry, bool directory = false, string filter = null)
{
if (parentEntry.Archive == null)
{
return Enumerable.Empty<ZipArchiveEntry>();
}
return GetChildren(parentEntry.Archive, parentEntry.FullName, directory, filter);
}
private static IEnumerable<ZipArchiveEntry> GetChildren(ZipArchive archive, string parentPath, bool directory = false, string filter = null)
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
string entryPath = entry.FullName;
if (string.Equals(entryPath, parentPath, StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
if (entryPath.StartsWith(parentPath))
{
string subpath = entryPath.Substring(parentPath.Length);
if (directory)
{
if (subpath.Count(c => ZipFileProvider.PathSeparators.Any(s => s == c)) == 1 &&
subpath.EndsWithAny(ZipFileProvider.StringPathSeparators))
{
yield return entry;
}
}
else
{
if (subpath.Count(c => ZipFileProvider.PathSeparators.Any(s => s == c)) == 0)
{
if (filter != null)
{
if (FileName.MatchPattern(subpath, filter, MatchPatternFlags.Normal))
{
yield return entry;
}
}
else
{
yield return entry;
}
}
}
}
}
}
public static bool IsDirectory(this ZipArchiveEntry entry)
{
if (entry == null)
{
return false;
}
return entry.FullName.EndsWithAny(ZipFileProvider.StringPathSeparators) && entry.Name == string.Empty;
}
public static bool EndsWithAny(this string value, string[] endings)
{
return endings.Any(value.EndsWith);
}
public static string NormalizedPath(this string path)
{
string result = path;
if (!string.IsNullOrEmpty(path))
{
result = path.Replace($"{ZipFileProvider.PathSeparator1}", $"{ZipFileProvider.PathSeparator2}");
}
return result;
}
}
}
using System.IO.Compression;
using System.Management.Automation;
namespace PSZip
{
internal class ZipFileDriveInfo : PSDriveInfo
{
public ZipArchive Archive { get; set; }
public ZipFileDriveInfo(PSDriveInfo driveInfo) : base(driveInfo) { }
public string NormalizedRoot => Root.NormalizedPath();
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Provider;
namespace PSZip
{
[CmdletProvider("ZipFile", ProviderCapabilities.Filter)]
public class ZipFileProvider : NavigationCmdletProvider
{
public const char PathSeparator1 = '/';
public const char PathSeparator2 = '\\';
public static readonly char[] PathSeparators = {
PathSeparator1, PathSeparator2
};
public static readonly string[] StringPathSeparators = {
$"{PathSeparator1}", $"{PathSeparator2}"
};
internal ZipFileDriveInfo ZipDrive => PSDriveInfo as ZipFileDriveInfo;
protected override PSDriveInfo NewDrive(PSDriveInfo drive)
{
try
{
ZipFileDriveInfo zipDriveInfo = new ZipFileDriveInfo(drive)
{
Archive = ZipFile.Open(drive.Root, ZipArchiveMode.Read)
};
return zipDriveInfo;
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "100", ErrorCategory.DeviceError, null));
return null;
}
}
protected override PSDriveInfo RemoveDrive(PSDriveInfo drive)
{
ZipFileDriveInfo driveInfo = drive as ZipFileDriveInfo;
driveInfo?.Archive.Dispose();
return driveInfo;
}
protected override bool IsValidPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
path = path.NormalizedPath();
string[] pathChunks = path.Split(PathSeparator2);
foreach (string pathChunk in pathChunks)
{
if (pathChunk.Length == 0)
{
return false;
}
}
return true;
}
protected override bool ItemExists(string path)
{
if (PathIsDrive(path))
{
return true;
}
path = path.NormalizedPath();
string archivePath = GetArchivePath(path);
if (ZipDrive == null || ZipDrive.Archive == null)
{
return false;
}
ZipArchiveEntry entry = ZipDrive.Archive.GetEntry(archivePath);
return entry != null;
}
protected override void GetChildItems(string path, bool recurse)
{
if (ZipDrive != null && ZipDrive.Archive != null)
{
WriteArchiveItems(path, recurse, 0);
}
}
private void WriteArchiveItems(string path, bool recurse, int depth)
{
if (PathIsDrive(path))
{
WriteDirectoryItems(ZipDrive.Archive.GetDirectories(), recurse, depth);
WriteFileItems(ZipDrive.Archive.GetFiles(Filter), depth);
return;
}
path = path.NormalizedPath();
string archivePath = GetArchivePath(path);
ZipArchiveEntry entry = ZipDrive.Archive.GetEntry(archivePath);
if (entry != null)
{
WriteDirectoryItems(entry.GetDirectories(), recurse, depth);
WriteFileItems(entry.GetFiles(Filter), depth);
}
}
private void WriteDirectoryItems(IEnumerable<ZipArchiveEntry> directories, bool recurse, int depth)
{
foreach (ZipArchiveEntry dirEntry in directories)
{
string padding = "".PadLeft(depth * 2, ' ');
string dirPath = Path.Combine(ZipDrive.NormalizedRoot, dirEntry.FullName);
WriteItemObject($"{padding}{dirEntry.GetName()}", dirPath, true);
if (ItemExists(dirPath) && recurse)
{
WriteArchiveItems(dirPath, true, depth + 1);
}
}
}
private void WriteFileItems(IEnumerable<ZipArchiveEntry> files, int depth)
{
foreach (ZipArchiveEntry fileEntry in files)
{
string padding = "".PadLeft(depth * 2, ' ');
string filePath = Path.Combine(ZipDrive.NormalizedRoot, fileEntry.FullName);
WriteItemObject($"{padding}{fileEntry.GetName()}", filePath, false);
}
}
protected override bool HasChildItems(string path)
{
path = path.NormalizedPath();
string archivePath = GetArchivePath(path);
if (ZipDrive == null || ZipDrive.Archive == null)
{
return false;
}
ZipArchiveEntry entry = ZipDrive.Archive.GetEntry(archivePath);
if (entry == null)
{
return false;
}
return entry.IsDirectory() && entry.GetDirectories().Any();
}
protected override bool IsItemContainer(string path)
{
if (PathIsDrive(path))
{
return true;
}
path = path.NormalizedPath();
string archivePath = GetArchivePath(path);
if (ZipDrive == null || ZipDrive.Archive == null)
{
return false;
}
ZipArchiveEntry entry = ZipDrive.Archive.GetEntry(archivePath);
if (entry == null)
{
return false;
}
return entry.IsDirectory();
}
private string GetArchivePath(string path)
{
if (path.StartsWith(ZipDrive.NormalizedRoot))
{
string subPath = path.Substring(ZipDrive.NormalizedRoot.Length).TrimStart(PathSeparators);
return subPath.Replace($"{PathSeparator2}", $"{PathSeparator1}");
}
return path;
}
private bool PathIsDrive(string path)
{
if (string.IsNullOrEmpty(path.Replace(ZipDrive.NormalizedRoot, "")))
{
return true;
}
foreach (string separator in StringPathSeparators)
{
if (string.IsNullOrEmpty(path.Replace(ZipDrive.NormalizedRoot + separator, "")))
{
return true;
}
}
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment