batch-filezipunziprarunrar

How to check if zip or rar file contains 1 or more files?


For the purposes of saving space and organizing, I'm zipping bunch of files in my local and networked folders. They are mainly CAD files, like stp, igs, etc.

There are already existing zip files and some are extracted by other users, but the zip file still exists on the folders, which eats up space.

Is there a command line zip, rar, 7z. etc. to find out if an archive file contains only 1 file?

I'd like to figure this out as I'll extract the archives with single files in to the current directory whilst extracting archives with 1+ files to \archivename\ folder. Otherwise one folder with 30 STP files, will suddenly have 30 folders and 30 files extracted in them which I don't want.

I currently use a batch file with WinRAR to extract and another program to check for duplicates, then WinRAR batch to re-zip them based on file extension. (Reason: people use different archive methods and there are duplicates of files all over.)

Sample batch files:

for /F "delims=," %%f in ('dir *.stp /B' ) do (%path% a -afzip -r- -m5 -ed -ep -or -tl -y -df "%%f".zip "%%f")

for /F "delims=;" %%f in ('dir *.7z /B /S' ) do (%path% x -OR -ilogC:\Users\XXXX\Desktop\myLog.txt "%%f" "%%~dpf"\"%%~nf"\)

Once I can check for number of files in a zip, I'll add a recursive function.

I can use NTFS compression, but I also want to organize the folders, some folder have 1000 files in them, I surely want to reduce that to 1. These are mainly for archiving purposes.

Any help or thought would be appreciated.


Solution

  • I suggest the following commented batch file for this task:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    
    rem Extract all 7-Zip, RAR and ZIP archives in current directory into
    rem subdirectories with name of archive file as name for subdirectory (-ad)
    rem with running WinRAR for extraction in background (-ibck) which means
    rem minimized to system tray with restoring also last access time (-tsa)
    rem and creation time (-tsc) if existing in archive file and with skipping
    rem files on extraction perhaps already present in the subdirectory with
    rem same last modification time (-u), but overwriting automatically older
    rem files in subdirectory if archive file contains an existing file with
    rem a newer last modification time (-y) ignoring all errors (also -y).
    
    for %%I in (7z rar zip) do "%ProgramFiles%\WinRAR\WinRAR.exe" x -ad -ibck -tsa -tsc -u -y *.%%I
    
    rem If a subdirectory contains only 1 file, move that file to the current
    rem directory with overwriting a perhaps already existing file with same
    rem name in current directory and then remove the subdirectory.
    
    for /D %%I in (*) do call :CheckSubDir "%%I"
    
    rem Exit processing of the batch file without fall through to subroutine.
    endlocal
    goto :EOF
    
    rem The subroutine CheckSubDir first checks for directories in directory
    rem passed as parameter to the subroutine. A directory containing at
    rem least one subdirectory is kept without any further processing.
    
    rem If the directory does not contain a subdirectory, it searches for files
    rem in the directory. If there are at least 2 files, the directory is kept
    rem without any further processing.
    
    rem But if the directory contains only 1 file, this file is moved to
    rem current directory. Then the empty directory is deleted before exiting
    rem the subroutine and continue batch file processing in calling loop.
    rem Each directory containing no subdirectory and no file is removed, too.
    
    :CheckSubDir
    for /F "delims=" %%D in ('dir /AD /B "%~1\*" 2^>nul') do goto :EOF
    setlocal EnableDelayedExpansion
    set FileCount=0
    for /F "delims=" %%F in ('dir /A-D /B "%~1\*" 2^>nul') do (
        set /A FileCount+=1
        if !FileCount! == 2 endlocal & goto :EOF
        set "FileName=%%F"
    )
    if %FileCount% == 1 move /Y "%~1\%FileName%" "%FileName%"
    rd "%~1"
    endlocal
    goto :EOF
    

    Please read the comments for details what this batch file does on execution using WinRAR.
    The batch file contains much more comment lines than real command lines.

    2>nul in the last two FOR loops redirects the error message output by command DIR to handle STDERR in case of no directory or no file found to device NUL to suppress it. The redirection operator > must be escaped here with character caret ^ to be interpreted as redirection operator on execution of DIR command line and not already on parsing the FOR command line.

    WinRAR supports many archive types on extraction. But WinRAR.exe is a GUI application and therefore does not support listing the contents of an archive file to console as Rar.exe supports.

    The console version Rar.exe as well as free console application UnRAR.exe support both listing the archive file contents to handle STDOUT in various formats.

    This difference on supported commands between WinRAR.exe and Rar.exe/UnRAR.exe can be seen by opening in WinRAR the help by clicking in menu Help on menu item Help topics, opening on help tab Contents the list item Command line mode, opening the list item Commands, clicking on list item Alphabetic commands list and comparing this list with the commands listed and described in text file Rar.txt in program files folder of WinRAR which is the manual for the console version.

    Rar.txt lists and describes:

    l[t[a],b] ... List archive contents [technical [all], bare]
    v[t[a],b] ... Verbosely list archive contents [technical [all], bare].

    Help of WinRAR does whether contain command l nor command v.

    It would be of course also possible to run Rar.exe or UnRAR.exe on each *.rar file with command lb, count the number of lines output as done in above batch file to count the files and extract the *.rar archive file depending on the line count to current directory (1 line only) or to a subdirectory.

    But it should be taken into account that on using bare list format and only 1 line output this line can be the name of an archived file or the name of an archived empty folder. The solution would be using standard list command and more analyze the attributes as well because a directory has attribute D while file does not have this attribute.

    And for *.7z and *.zip files the same must be coded using 7z.exe or 7za.exe. The help of 7-Zip describes also the available commands and switches like the help of WinRAR.

    But all those efforts do not make much sense in comparison to posted solution as the archive file has to be extracted at all and moving a file is done very fast as just the entry in file allocation table is modified and no data are copied or moved at all.

    Running 7-Zip or Rar separately for first just listing each archive file contents, analyzing the list, and running 7-Zip or Rar once again on archive file for extraction is much slower than running WinRAR just 3 times (or less) to extract all archives and then move some files and remove some directories.

    For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

    See also the Microsoft TechNet article Using command redirection operators.