7-Zip has two very useful right-click menu options for archives:
- Extract Here
- Extract to "MyZip\" (i.e. extract to subfolder)
- If an archive contains one file/folder only (in the root), I would like it to do Extract Here.
- If an archive contains multiple files and/or folders (in the root), I would like it to do Extract to subfolder.
- If you always do Extract Here, you could end up with a mess if the zip contains lots of loose files not within a folder.
- If you always do Extract to subfolder, but everything is already neatly contained within one folder, you end up with an unnecessary extra folder level, so you have to move everything up one level, delete the unnecessary folder, and possibly rename the top-level folder.
This same problem is mentioned here:
7-Zip / Discussion / Open Discussion:Feature Req: Conditional extract-to-folder
https://sourceforge.net/p/sevenzip/disc ... /efcad42a/
7-Zip is one of the best utilities available, but this problem really needs to be fixed.
This may be the only time in human history that 'smart' behaviour in an IT context would be a good idea.
One solution would be to count the number of files and folders in the root, but I was unable to do this reliably via the 7-Zip command line because there appear to be some limitations in 7-Zip's syntax for listing files and folders, but also I found some inconsistencies in what 7-Zip would list for different archives, with no clear pattern.
Here's an attempt:
Code: Select all
;l (List contents of archive) command
;https://sevenzip.osdn.jp/chm/cmdline/commands/list.htm
;7-Zip / Discussion / Open Discussion:Listing only the top level of a ZIP file?
;https://sourceforge.net/p/sevenzip/discussion/45797/thread/843b2e18/
;7-Zip / Discussion / Open Discussion:Feature Req: Conditional extract-to-folder
;https://sourceforge.net/p/sevenzip/discussion/45797/thread/efcad42a/
;Index of /download/1.1
;https://autohotkey.com/download/1.1/
;AutoHotkey_1.1.26.01.zip
;https://autohotkey.com/download/1.1/AutoHotkey_1.1.26.01.zip
q:: ;7-Zip - attempt to list files and folders in the root
vPathZip = %A_Desktop%\AutoHotkey_1.1.26.01.zip
vPathExe = C:\Program Files\7-Zip\7z.exe
vTarget = "%vPathExe%" l "%vPathZip%"
vSfx := " -x!*\*"
;vSfx := " -x!*\"
vText := JEE_RunGetStdOut(vTarget vSfx)
MsgBox, % vText
vCount := vCountDir := vCountFile := 0
Loop, Parse, vText, `n
{
if (SubStr(A_LoopField, 1, 19) = "-------------------")
vCount++
else if (vCount = 1)
if (SubStr(A_LoopField, 21, 1) = "D")
vCountDir++
else
vCountFile++
}
MsgBox, % vCountDir " " vCountFile
return
;==================================================
JEE_RunGetStdOut(vTarget, vSize:="")
{
DetectHiddenWindows, On
vComSpec := A_ComSpec ? A_ComSpec : ComSpec
Run, % vComSpec,, Hide, vPID
WinWait, % "ahk_pid " vPID
DllCall("kernel32\AttachConsole", UInt,vPID)
oShell := ComObjCreate("WScript.Shell")
oExec := oShell.Exec(vTarget)
vStdOut := ""
if !(vSize = "")
VarSetCapacity(vStdOut, vSize)
while !oExec.StdOut.AtEndOfStream
vStdOut := oExec.StdOut.ReadAll()
DllCall("kernel32\FreeConsole")
Process, Close, % vPID
return vStdOut
}
;==================================================
So, an alternative solution is to always extract to a temporary folder, then count the files/folders, then move to the folder, or to a subfolder, as appropriate.
A function with an example.
Code: Select all
q:: ;7-Zip - extract to folder/subfolder based on the number of items inside the archive
vPathZip = %A_Desktop%\MyZip.zip
vDirDest = %A_Desktop%\%A_Now%
vPathExe := "C:\Program Files\7-Zip\7z.exe"
vDirTemp = %A_Desktop%\z 7-zip temp
vRet := JEE_7ZipExtractEx(vPathZip, vDirDest, vPathExe, vDirTemp, vPathDest)
MsgBox, % vRet " " vPathDest
vComSpec := A_ComSpec ? A_ComSpec : ComSpec
vAttrib := FileExist(vPathDest)
;if the zip contained one file at its root, extract the file and perform Open Containing Folder
;if the zip contained one folder at its root, extract the folder and open that folder
;if the zip contained multiple files/folders at its root, extract them to a folder, and open that folder
if !vAttrib
return
else if InStr(vAttrib, "D")
Run, % vPathDest
else
Run, %vComSpec% /c explorer.exe /select`, "%vPathDest%",, Hide
return
*/
;if zip contains 1 file/folder only in its root, extract directly
;if zip contains multiple files/folders in its root, extract to subfolder
;extract file/folder based on number of items inside archive
JEE_7ZipExtractEx(vPathZip, vDirDest, vPathExe, vDirTemp, ByRef vPathDest)
{
vPathDest := ""
if !FileExist(vPathZip) || !FileExist(vPathExe)
return 0
if (vDirDest = "")
SplitPath, vPathZip,, vDirDest
vDirTemp .= "\X"
if FileExist(vDirTemp)
FileRecycle, % vDirTemp
;FileDelete, % vDirTemp
if FileExist(vDirTemp)
return 0
FileCreateDir, % vDirTemp
if !InStr(FileExist(vDirTemp), "D")
return 0
if !FileExist(vDirDest)
FileCreateDir, % vDirDest
if !InStr(FileExist(vDirDest), "D")
return 0
;-y (assume Yes on all queries)
vTarget = "%vPathExe%" x "%vPathZip%" -o"%vDirTemp%" -y
RunWait, % vTarget,, Hide
vCount := 0, vIsDir := 1
Loop, Files, % vDirTemp "\*", FD
{
vPath := A_LoopFileFullPath
vName := A_LoopFileName
vCount := A_Index
if (A_Index = 2)
break
}
if (vCount = 0)
return 0
else if (vCount = 1) ;move the 1 item contained in the zip
{
vPathDest := vDirDest "\" vName
if FileExist(vPathDest)
return 0
else if InStr(FileExist(vPath), "D")
FileMoveDir, % vPath, % vPathDest
else
FileMove, % vPath, % vPathDest
}
else ;create a folder for the multiple items contained in the zip
{
SplitPath, vPathZip,,,, vNameNoExtZip
vPathDest := vDirDest "\" vNameNoExtZip
if FileExist(vPathDest)
return 0
FileMoveDir, % vDirTemp, % vPathDest
}
return !!FileExist(vPathDest)
}
Code: Select all
;'7-Zip extract.ahk' by jeeswg
;extract to folder/subfolder based on the number of items inside the archive
#NoTrayIcon
vPathZip = %1%
SplitPath, vPathZip,, vDirDest
;if no path specified, add a shortcut to the SendTo folder
if (vPathZip = "")
{
vName := "7-Zip extract.lnk"
vPathLnk = %A_AppData%\Microsoft\Windows\SendTo\%vName%
MsgBox, % "creating shortcut: " vPathLnk
FileCreateShortcut, % A_AhkPath, % vPathLnk,, "%A_ScriptFullPath%"
return
}
vPathExe := "C:\Program Files\7-Zip\7z.exe"
vDirTemp = %A_Desktop%\z 7-zip temp
vRet := JEE_7ZipExtractEx(vPathZip, vDirDest, vPathExe, vDirTemp, vPathDest)
MsgBox, % vRet " " vPathDest
vComSpec := A_ComSpec ? A_ComSpec : ComSpec
vAttrib := FileExist(vPathDest)
;if the zip contained one file, extract the file and perform Open Containing Folder
;if the zip contained one folder at its root, extract the folder and open that folder
;if the zip contained multiple files/folders at its root, extract them to a folder, and open that folder
if !vAttrib
return
else if InStr(vAttrib, "D")
Run, % vPathDest
else
Run, %vComSpec% /c explorer.exe /select`, "%vPathDest%",, Hide
return
;==================================================
;if zip contains 1 file/folder only in its root, extract directly
;if zip contains multiple files/folders in its root, extract to subfolder
;extract file/folder based on number of items inside archive
JEE_7ZipExtractEx(vPathZip, vDirDest, vPathExe, vDirTemp, ByRef vPathDest)
{
vPathDest := ""
if !FileExist(vPathZip) || !FileExist(vPathExe)
return 0
if (vDirDest = "")
SplitPath, vPathZip,, vDirDest
vDirTemp .= "\X"
if FileExist(vDirTemp)
FileRecycle, % vDirTemp
;FileDelete, % vDirTemp
if FileExist(vDirTemp)
return 0
FileCreateDir, % vDirTemp
if !InStr(FileExist(vDirTemp), "D")
return 0
if !FileExist(vDirDest)
FileCreateDir, % vDirDest
if !InStr(FileExist(vDirDest), "D")
return 0
;-y (assume Yes on all queries)
vTarget = "%vPathExe%" x "%vPathZip%" -o"%vDirTemp%" -y
RunWait, % vTarget,, Hide
vCount := 0, vIsDir := 1
Loop, Files, % vDirTemp "\*", FD
{
vPath := A_LoopFileFullPath
vName := A_LoopFileName
vCount := A_Index
if (A_Index = 2)
break
}
if (vCount = 0)
return 0
else if (vCount = 1) ;move the 1 item contained in the zip
{
vPathDest := vDirDest "\" vName
if FileExist(vPathDest)
return 0
else if InStr(FileExist(vPath), "D")
FileMoveDir, % vPath, % vPathDest
else
FileMove, % vPath, % vPathDest
}
else ;create a folder for the multiple items contained in the zip
{
SplitPath, vPathZip,,,, vNameNoExtZip
vPathDest := vDirDest "\" vNameNoExtZip
if FileExist(vPathDest)
return 0
FileMoveDir, % vDirTemp, % vPathDest
}
return !!FileExist(vPathDest)
}
;==================================================