1
votes

Fichier batch utilisant 7zip pour extraire les zips imbriqués et supprimer les zips après une extraction réussie

J'ai un dossier plein de fichiers zip. Ces fichiers zip contiennent parfois des fichiers zip, qui contiennent parfois des fichiers zip, etc. J'essaie d'écrire un fichier de commandes que je peux coller dans le dossier supérieur contenant tous les zips, et quand il s'exécute, il décompressera tous les fichiers zip imbriqués, et dans les sous-répertoires, tout en bas, et supprimera les zips une fois ils ont été extraits avec succès. Les chemins d'accès complets aux fichiers doivent être préservés. S'il y a une erreur et qu'un fichier ne peut pas être extrait, il ne doit pas être supprimé et le fichier et le chemin du fichier doivent être imprimés dans un fichier texte.

Jusqu'à présent, j'ai ceci:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ErrorOutput="
set "LoopCount=20"

rem The current directory is used on batch file being called without
rem a base folder path or with just one or more double quotes.
set "BaseFolder=%~1"
if defined BaseFolder set "BaseFolder=%BaseFolder:"=%"
if not defined BaseFolder set "BaseFolder=%CD%" & goto VerifyFolderPath

rem Make sure the folder path contains backslashes and not forward slashes
rem and does not contain wildcard characters or redirection operators or a
rem horizontal tab character after removing all double quotes.
set "BaseFolder=%BaseFolder:/=\%"
for /F "delims=*?|<>    " %%I in ("%BaseFolder%") do if not "%BaseFolder%" == "%%I" (
    echo ERROR: %~nx0 must be called with a valid folder path.
    echo        "%~1" is not a valid folder path.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Get full folder path in case of the folder was specified with
rem a relative path. If the folder path references the root of a
rem drive like on using "C:\" or just "\", redefine the folder
rem path with full path for root of the (current) drive.
for %%I in ("%BaseFolder%") do set "BaseFolder=%%~fI"

:VerifyFolderPath
rem The base folder path must end with a backslash for verification.
if not "%BaseFolder:~-1%" == "\" set "BaseFolder=%BaseFolder%\"

rem Verify the existence of the folder. The code above processed also
rem folder paths of folders not existing at all and also invalid folder
rem paths containing for example a colon not (only) after drive letter.
if not exist "%BaseFolder%" (
    echo ERROR: Folder "%BaseFolder%" does not exist.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Make sure to process all ZIP files existing in base folder and all
rem its subfolders by setting archive file attribute on all ZIP files.
%SystemRoot%\System32\attrib.exe +A /S "%BaseFolder%*.zip"

rem Process all *.zip files found in base folder and all its subfolders
rem which have the archive file attribute set. *.zip files with archive
rem file attribute not set are ignored to avoid an endless running loop
rem if a ZIP archive file cannot be extracted successfully with reason(s)
rem output by 7-Zip or if the ZIP file cannot be deleted after successful
rem extraction of the archive. The archive extraction loop runs are limited
rem additionally by a loop counter as defined at top of the batch file for
rem 100% safety on prevention of an endless loop execution.

:ExtractArchives
set "ArchiveProcessed="
for /F "delims=" %%I in ('dir "%BaseFolder%*.zip" /AA-D /B /S 2^>nul') do (
    set "ArchiveProcessed=1"
    echo Extracting archive: "%%I"
    "%ProgramFiles%\7-Zip\7z.exe" x -bd -bso0 -o"%%~dpnI\" -spd -y -- "%%I"
@pause
    if errorlevel 255 set "ErrorOutput=1" & goto EndBatch
    if errorlevel 1 (
        set "ErrorOutput=1"
        %SystemRoot%\System32\attrib.exe -A "%%I"
    ) else (
        del /A /F "%%I"
        if exist "%%I" (
            echo ERROR: Failed to delete: "%%I"
            set "ErrorOutput=1"
            %SystemRoot%\System32\attrib.exe -A "%%I"
        )
    )
)
if not defined ArchiveProcessed goto EndBatch
set /A LoopCount-=1
if not LoopCount == 0 goto ExtractArchives

:EndBatch
if defined ErrorOutput echo/& pause
endlocal
echo[
echo[
echo If no errors are displayed above, everything extracted successfully. Remember to delete the batch file once you are done.
@pause

Que je peux déposer dans un dossier et exécuter, il décompressera le premier niveau de zips mais aucun des zips imbriqués à l'intérieur. C'est le premier obstacle.

Le prochain obstacle serait de supprimer les zips extraits avec succès. Et enfin, ne pas supprimer les zips qui n'ont pas pu être extraits et imprimer leur nom et / ou chemin vers un fichier texte.

Toutes les suggestions ou morceaux de code sont appréciés. Ou s'il existe une meilleure façon de le faire entièrement.

**** MIS À JOUR ****

Mofi a publié une réponse qui semble fonctionner à l'exception d'un élément:

Lorsqu'un ZIP est extrait, il doit être extrait dans un dossier du même nom, afin que je puisse toujours suivre la structure.

Exemple de départ:

SET "filename=%~1"
SET dirName=%filename:~0,-4%

7z x -o"%dirName%" "%filename%"

Doit devenir ceci:

[Top Level Folder Holding Zips] (folder)
--ExampleZip (folder)
---FileInZip.txt
---FileinZip2.txt
--ExampleZip2 (folder)
---Folder1 (folder)
----ExampleZip3 (folder)
-----FileinZip3.txt
-----FileinZip4.txt
---ExampleZip4 (folder)
----FileinZip5.txt
----FileinZip6.txt

La structure complète est donc toujours visible.

Je pense que la première réponse à cette question montre ce que je dois inclure: Extraire le contenu du zip dans le répertoire avec le même nom que le fichier zip, conserver la structure du répertoire

Cette partie:

[Top Level Folder Holding Zips] (folder)
--ExampleZip.zip
---FileInZip.txt
---FileinZip2.txt
--ExampleZip2.zip
---Folder1 (folder)
----ExampleZip3.zip
-----FileinZip3.txt
-----FileinZip4.txt
---ExampleZip4.zip
----FileinZip5.txt
----FileinZip6.txt

Doit être écrasé quelque part. Ou il semble qu'il devrait y avoir un commutateur pour 7Zip qui le fasse, puisque vous pouvez le faire à partir du menu contextuel avec "Extraire vers *" Je pensais que c'était ce que faisait la commande "Extraire avec les chemins complets" mais cela doit avoir quelque chose à faire avec le commutateur -o, spécifiant le chemin de sortie? Comment spécifier le chemin de sortie comme un dossier avec le même nom que le zip d'entrée? Ou fusionner la réponse de cette question que j'ai liée à la réponse de Mofi?

*** NOUVEAU MIS À JOUR ***

Je pensais qu'il y avait un problème avec le fichier de commandes ignorant les fichiers ZIP avec des traits de soulignement dans le nom, mais c'était une coïncidence et il ignorait en fait les fichiers ZIP sans l'attribut du fichier d'archive.

Mofi a suggéré un autre correctif pour ce qui a fonctionné, mais le fichier de commandes n'extrait pas les zips imbriqués qui nécessitaient le jeu d'attributs de fichier d'archive.

Cela fonctionne en quelque sorte, dans la mesure où je peux exécuter manuellement le fichier de commandes plusieurs fois et cela fonctionnera tout au long du dossier, mais le calcul de la boucle ne semble pas fonctionner, ou est en train de calculer / se terminer avant que le fichier de commandes ne soit défini l'attribut Archive pour tous les fichiers zip?

Voici la version actuelle avec laquelle je travaille:

@ECHO ON

SET source=%cd%
FOR /F "TOKENS=*" %%F IN ('DIR /S /B "%source%\*.zip"') DO "C:\Program Files\7-Zip\7z.exe" x "%%~fF" -o"%%~pF\"
EXIT

Il est rare qu'il y ait peut-être 10 ou 20 couches de zips imbriqués, donc une solution rapide et sale peut être juste de boucler tout le fichier batch 10 ou 20 fois, à moins que ce ne soit une mauvaise idée ou qu'il existe un moyen plus élégant de fais le.


0 commentaires

3 Réponses :


1
votes

Utilisation de Groovy ou Ant

Ce serait beaucoup plus facile en utilisant Apache Ant ou, mieux encore, le Groovy AntBuilder .

Par exemple, ce script Groovy décompressera tous les fichiers zip de niveau supérieur puis les supprimera:

// let's create a scanner of filesets
def scanner = ant.fileScanner {
    fileset(dir:"src/test") {
        include(name:"**/My*.groovy")
    }
}

// now let's iterate over
def found = false
for (f in scanner) {
    println("Found file $f")
    found = true
    assert f instanceof File
    assert f.name.endsWith(".groovy")
}
assert found

Vous devrez continuer à scanner le dossier de destination pour les zips et à répéter le processus ci-dessus, jusqu'à ce que tout soit décompressé. Vous pouvez également trouver utile d'utiliser un FileScanner.

AntBuilder lève une exception en cas d'échec de quelque chose, vous pouvez donc éviter de supprimer les archives dont la décompression échoue. AntBuilder enregistrera également sa progression, en utilisant les mécanismes de journalisation Java standard. Vous pouvez lui indiquer le niveau de détail souhaité ou le supprimer complètement

La documentation complète d'AntBuilder est ici:

Utilisation d'un FileScanner

Exemple tiré de la documentation Groovy AntBuilder:

new AntBuilder().with {

  def sourceRoot = '.'

  // Unzip all .zip files in / underneath sourceRoot
  unzip( dest: 'some-folder' ) {
    fileset( dir: sourceRoot ) {
      include name: "**/*.zip"
    }
  }

  // Unzip throws an exception on failure.
  // Delete all .zip files in / underneath sourceRoot
  delete {
    fileset( dir: sourceRoot, includes: '**/*.zip' )
  }
}

Mettre ensemble

Ce n'est pas un grand pas en avant de combiner un FilesScanner avec un AntBuilder pour faire le travail. Je soupçonne que ce sera beaucoup plus facile que de le faire avec un script batch.


2 commentaires

La raison pour laquelle j'essaie d'utiliser un fichier de commandes est qu'il est facile à exécuter avec le flux de travail / les ordinateurs sur lesquels je dois l'exécuter. Je ne sais rien de Groovy ou d'AntBuilder (pas que je sache grand-chose non plus sur les fichiers batch) - mais finiraient-ils par produire quelque chose que je peux utiliser de la même manière qu'un fichier batch? Donner à un utilisateur de glisser-déposer dans un dossier, d'exécuter et d'en finir avec tout?


Groovy peut être compilé dans un fichier jar exécutable. De cette façon, il n'est pas nécessaire d'avoir Groovy sur l'ordinateur de l'utilisateur. Tant qu'ils ont installé Java, ils peuvent exécuter des programmes Groovy comme n'importe quel autre programme Java.



1
votes

La tâche d'extraire récursivement toutes les archives ZIP, y compris les archives ZIP imbriquées dans une archive ZIP, peut être réalisée en exécutant le processus d'extraction de fichier d'archive ZIP en boucle jusqu'à ce qu'il n'y ait plus de fichier ZIP. Mais il doit y avoir au moins deux cas d'utilisation pris en compte pour éviter une boucle d'extraction d'archive en cours d'exécution sans fin:

  1. L'extraction d'un fichier d'archive ZIP échoue pour une raison quelconque. 7-Zip génère des informations sur la ou les raisons de l'erreur. Un tel fichier ZIP ne doit pas être traité une seconde fois.
  2. La suppression d'un fichier ZIP extrait avec succès échoue pour une raison quelconque. Le fichier ZIP ne doit pas être traité à nouveau.

La solution traite uniquement les fichiers ZIP avec l'attribut de fichier d'archive défini comme fait automatiquement par Windows lors de la création, du changement de nom ou de la modification d'un fichier et supprime l'attribut de fichier d'archive sur chaque fichier ZIP sur lequel le processus d'extraction ou la suppression du fichier a échoué pour éviter le traitement le fichier ZIP à nouveau.

L'attribut de fichier d'archive est défini sur tous les fichiers * .zip de l'arborescence de répertoires à traiter avant de démarrer le processus d'extraction des fichiers d'archive pour s'assurer que tous les fichiers * .zip existants sont traités au moins une fois. L'attribut de fichier d'archive est également défini sur tous les fichiers * .zip dans le répertoire de sortie d'un fichier d'archive ZIP complètement traité avec succès pour s'assurer que même les fichiers * .zip dans un fichier ZIP avec l'attribut de fichier d'archive non défini après l'extraction sont également traités au prochain boucle d'extraction de fichier d'archive exécutée.

"%ProgramFiles%\7-Zip\7z.exe" x -bd -bso0 -o"%%~dpnI\" -spe -spd -y -- "%%I"

Remarque: il doit y avoir un caractère de tabulation horizontale après "delims=*?|<> Et " à la ligne 16 du code du fichier de commandes et non une série de caractères d'espace comme il y en aura après avoir copié le code de la fenêtre du navigateur et collé le code dans une fenêtre d'éditeur de texte.

Le fichier batch est commenté avec des lignes avec la commande REM (remarque). Ces commentaires doivent être lus pour comprendre le code et peuvent ensuite être supprimés pour une exécution plus efficace du fichier de commandes par le processeur de commandes Windows.

Les commutateurs 7-Zip utilisés dans le code sont expliqués à l'aide de 7-Zip ouvert en double-cliquant sur le fichier 7-zip.chm ou en ouvrant l' aide depuis la fenêtre GUI de 7-Zip démarré. Sur l'onglet Aide Contenu développez l'élément de liste Version de ligne de commande et cliquez sur Commutateurs d' élément de liste pour afficher la page d'aide Commutateurs de ligne de commande avec tous les commutateurs pris en charge par la version actuellement utilisée de 7-Zip .

Le fichier batch peut être exécuté avec un chemin de dossier comme argument pour traiter tous les fichiers ZIP de ce dossier et de tous ses sous-dossiers. Il est donc possible d'ajouter au menu contextuel Envoyer au de l' Explorateur de Fichiers Windows un fichier de raccourci qui exécute le fichier batch avec le chemin du dossier passé par l' Explorateur de Fichiers Windows au fichier batch comme premier argument. Il serait également possible d'enregistrer le fichier de commandes en tant qu'option de menu contextuel pour le Directory dans le registre Windows afin de pouvoir exécuter le fichier de commandes facilement à partir de n'importe quelle application prenant en charge les gestionnaires de menu contextuel de Windows pour un répertoire.

Modifier après la question modifiée: La ligne de commande exécutant 7-Zip peut être modifiée pour:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ErrorOutput="
set "LoopCount=20"

rem The current directory is used on batch file being called without
rem a base folder path or with just one or more double quotes.
set "BaseFolder=%~1"
if defined BaseFolder set "BaseFolder=%BaseFolder:"=%"
if not defined BaseFolder set "BaseFolder=%CD%" & goto VerifyFolderPath

rem Make sure the folder path contains backslashes and not forward slashes
rem and does not contain wildcard characters or redirection operators or a
rem horizontal tab character after removing all double quotes.
set "BaseFolder=%BaseFolder:/=\%"
for /F "delims=*?|<>    " %%I in ("%BaseFolder%") do if not "%BaseFolder%" == "%%I" (
    echo ERROR: %~nx0 must be called with a valid folder path.
    echo        "%~1" is not a valid folder path.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Get full folder path in case of the folder was specified with
rem a relative path. If the folder path references the root of a
rem drive like on using "C:\" or just "\", redefine the folder
rem path with full path for root of the (current) drive.
for %%I in ("%BaseFolder%") do set "BaseFolder=%%~fI"

:VerifyFolderPath
rem The base folder path must end with a backslash for verification.
if not "%BaseFolder:~-1%" == "\" set "BaseFolder=%BaseFolder%\"

rem Verify the existence of the folder. The code above processed also
rem folder paths of folders not existing at all and also invalid folder
rem paths containing for example a colon not (only) after drive letter.
if not exist "%BaseFolder%" (
    echo ERROR: Folder "%BaseFolder%" does not exist.
    set "ErrorOutput=1"
    goto EndBatch
)

rem Make sure to process all ZIP files existing in base folder and all
rem its subfolders by setting archive file attribute on all ZIP files.
%SystemRoot%\System32\attrib.exe +A /S "%BaseFolder%*.zip" >nul

rem Process all *.zip files found in base folder and all its subfolders
rem which have the archive file attribute set. *.zip files with archive
rem file attribute not set are ignored to avoid an endless running loop
rem if a ZIP archive file cannot be extracted successfully with reason(s)
rem output by 7-Zip or if the ZIP file cannot be deleted after successful
rem extraction of the archive. The archive extraction loop runs are limited
rem additionally by a loop counter as defined at top of the batch file for
rem 100% safety on prevention of an endless loop execution.

:ExtractArchives
set "ArchiveProcessed="
for /F "delims=" %%I in ('dir "%BaseFolder%*.zip" /AA-D /B /S 2^>nul') do (
    set "ArchiveProcessed=1"
    echo Extracting archive: "%%I"
    "%ProgramFiles%\7-Zip\7z.exe" x -bd -bso0 -o"%%~dpI" -spd -y -- "%%I"
    if errorlevel 255 set "ErrorOutput=1" & goto EndBatch
    if errorlevel 1 (
        set "ErrorOutput=1"
        %SystemRoot%\System32\attrib.exe -A "%%I"
    ) else (
        %SystemRoot%\System32\attrib.exe +A /S "%%~dpnI\*.zip" >nul
        del /A /F "%%I"
        if exist "%%I" (
            echo ERROR: Failed to delete: "%%I"
            set "ErrorOutput=1"
            %SystemRoot%\System32\attrib.exe -A "%%I"
        )
    )
)
if not defined ArchiveProcessed goto EndBatch
set /A LoopCount-=1
if not LoopCount == 0 goto ExtractArchives

:EndBatch
if defined ErrorOutput echo/& pause
endlocal

Chaque fichier ZIP est extrait avec cette ligne de commande dans un sous-dossier du dossier du fichier ZIP avec le nom du fichier ZIP en remplaçant -o"%%~dpI" par -o"%%~dpnI\" . Le commutateur 7-Zip supplémentaire -spe évite de dupliquer le nom du dossier si le fichier ZIP contient au niveau supérieur un dossier portant le même nom que le fichier ZIP. Donc, si Example3.zip contient au niveau supérieur le dossier Example3 , les fichiers sont extraits dans le dossier Example3 et non dans le dossier Example3\Example3 comme cela se produirait sans l'utilisation de l'option -spe .

Pour comprendre les commandes utilisées et leur fonctionnement, ouvrez une fenêtre d' invite de commande , exécutez les commandes suivantes et lisez attentivement toutes les pages d'aide affichées pour chaque commande.

  • attrib /?
  • call /?
  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • rem /?
  • set /?
  • setlocal /?

Lisez la documentation Microsoft sur l' utilisation des opérateurs de redirection de commande pour une explication de 2>nul . L'opérateur de redirection > doit être échappé avec le caractère caret ^ sur la ligne de commande FOR pour être interprété comme un caractère littéral lorsque l'interpréteur de commandes Windows traite cette ligne de commande avant d'exécuter la commande FOR qui exécute la ligne de commande dir intégrée dans un processus de commande distinct démarré en arrière-plan.


0 commentaires

0
votes

Enfin réussi à écrire un fichier de commandes qui peut décompresser les zips imbriqués, en gardant la structure du fichier d'archive intacte!

la logique est que, exécutez récursivement jusqu'à ce que tous les fichiers zip soient décompressés. Le nombre d'itérations par défaut est de 5 et peut être passé en tant que cmd arg "extract.bat 3". peut être changé en boucle while jusqu'à ce que le fichier de hit ne trouve pas d'exception. Et surtout, supprimez le fichier d'archive après l'extraction, nous ne nous engageons donc pas dans une boucle sans fin! Mais suivez les règles ci-dessous

  1. il utilise 7z, assurez-vous que dans la fenêtre cmd 7z peut être exécuté, c'est-à-dire dans le chemin
  2. Les noms de fichier zip ne peuvent pas contenir d'espaces. assurez-vous de cela et ext est zip
  3. copiez le fichier zip dans un répertoire où il n'y a pas d'autres fichiers zip
  4. Et seulement .zip ext, vous pouvez le changer en rar ou quoi que ce soit dans le fichier de commandes

Voici le fichier batch

Rem Nested unzip - @sivakd
echo off
if  "%1"=="" (set iter=5) else (set iter=%1)
echo Running  %iter% iterations
for /l %%x in (1, 1, %iter%) do (
    dir *.zip /s /b > ziplist.txt
    for /F %%f in (ziplist.txt) do (
        7z x %%f -o%%~dpnf -y & del /f %%f
    )
    del ziplist.txt
)


0 commentaires