Last active
March 24, 2022 20:02
-
-
Save Beej126/3064e77ddb5fad34ff04ee40ffec16bc to your computer and use it in GitHub Desktop.
Windows File Explorer auto extract zips like Mac Finder
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
function CheckFolderExistsAndCreate(folder) { | |
if (fso.FolderExists(folder)) { | |
WScript.Echo("destination folder already exists: " + folder); | |
WScript.Quit(1); | |
} | |
fso.CreateFolder(folder); | |
} | |
//probe the zip file for depth/complexity | |
//report back if it is a simple folder to immediately expand into concrete files | |
//or prompt for just viewing the zip as-is | |
function isBiggish(items, depth, why) { | |
var folderCount = 0; | |
var fileCount = 0; | |
//WScript.Echo("foldercount: " + folderCount + ", depth: " + depth + ", count: " + items.Count); | |
if (depth === 2) { why.why = "is at least 3 folders deep"; return true; } | |
for (var i = 0; i < items.Count; i++) { | |
var thisitem = items.Item(i); | |
//these don't count, would like to avoid them on the extract as well but that's even more "unecessary" enumeration | |
if (thisitem.Name == "__MACOSX") continue; | |
//WScript.Echo("depth: " + depth + ", name: " + thisitem.Name); | |
if (thisitem.IsFolder) { | |
folderCount++; | |
if (folderCount > 3) { why.why = "has more than 3 folders"; return true; } | |
if (isBiggish(thisitem.GetFolder.Items(), depth + 1, why)) { /*we already have a why from call stack*/ return true; } | |
} | |
if (!thisitem.IsFolder && ++fileCount > 5) { why.why = "has more than 5 files"; return true; } | |
if (fso.GetExtensionName(thisitem) == "zip") { why.why = "has zips inside"; return true; }; | |
} | |
return false; | |
} | |
var objArgs = WScript.Arguments; | |
if (objArgs.length === 0) { | |
WScript.Echo("please pass zip file as first and only argument") | |
WScript.Quit(1); | |
} | |
var fso = new ActiveXObject("Scripting.FileSystemObject"); | |
var sh = new ActiveXObject("Shell.Application"); | |
var zipFileFullPath = objArgs.Item(0); | |
var zipItems = sh.NameSpace(zipFileFullPath).Items(); | |
// if empty, bail out | |
if (zipItems.Count === 0) { | |
WScript.Echo("zip file empty. nothing to do."); | |
WScript.Quit(0); | |
} | |
var fileObj = fso.GetFile(zipFileFullPath); | |
var zipFileName = fso.GetBaseName(fileObj); //no path nor extension | |
var parentPath = fso.GetParentFolderName(fileObj); //yep this is full path not just individual FolderName, despite the name of the method!? | |
var extractToPath = parentPath + "\\" + zipFileName; | |
// cool convenience - pull files up out of single nested root folder | |
var firstItem = zipItems.Item(0); | |
if (zipItems.Count === 1) { | |
if (firstItem.IsFolder) { | |
zipItems = firstItem.GetFolder.Items(); | |
//removed: zipFileName = firstItem.Name; | |
//keep full zip file name for single root folders | |
//this often preserves a more specfic zip file name with for example version numbers | |
} | |
else { | |
//single file inside zip, don't even create a subfolder, just put it right at the path where the zip itself is | |
extractToPath = parentPath; | |
} | |
} | |
var whyIsBig = { why: null }; | |
// if many files, prompt for full extract versus just view | |
if (isBiggish(zipItems, 0, whyIsBig)) { | |
// https://ss64.com/vb/popup.html | |
var wsh = new ActiveXObject("WScript.Shell"); | |
var result = wsh.Popup( | |
"This zip file " + whyIsBig.why + ".\n\n" + | |
"Yes = Extract All & ** DELETE .zip **\n" + | |
"No = Simply view the zip contents and leave it intact", | |
0 /* seconds to wait, 0 = no timeout */, | |
"Extract or View", | |
3 /* Yes / No / Cancel */ + 32 /* question mark icon */ | |
); | |
switch (result) { | |
case 2 /*cancel*/: WScript.Quit(1); break; | |
case 7 /* no */: | |
//we have to specially fire the "open" verb via ShellExecute since we've overridden the default that would fire via sh.Open() | |
sh.ShellExecute(zipFileFullPath, "" /*args*/, "" /*working dir*/, "open" /*operation aka "verb"*/, 1 /*window style, 1=normal*/); | |
WScript.Quit(1); | |
break; | |
} | |
} | |
extractToPath !== parentPath && CheckFolderExistsAndCreate(extractToPath); | |
// finally! we get to do the extract | |
sh.NameSpace(extractToPath).Copyhere(zipItems); | |
// automatically pop open file explorer in our new folder, nice!! :) | |
sh.Open(extractToPath); | |
// delete the zip file, to recycle bin | |
// (FYI fso.DeleteFile() deletes permanently, doesn't preserve in recycle bin) | |
sh.NameSpace(parentPath).ParseName(zipFileName + ".zip").InvokeVerb("Delete"); |
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
::create the entry | |
reg add "HKEY_CURRENT_USER\Software\Classes\CompressedFolder\shell\AutoUnZip" /f /ve /d "AutoUnZip" | |
::and the actual command to execute | |
reg add "HKEY_CURRENT_USER\Software\Classes\CompressedFolder\shell\AutoUnZip\command" /f /ve /t REG_EXPAND_SZ /d "wscript /e:javascript \"%~dp0AutoUnZip.wsh.js\" \"^%%L\"" | |
::set as the default command on .zip file double click | |
reg add "HKEY_CURRENT_USER\Software\Classes\CompressedFolder\shell" /f /ve /t REG_SZ /d AutoUnZip | |
@timeout /t 5 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment