• XSS.stack #1 – первый литературный журнал от юзеров форума

cmd/bat распаковка архива с автоудалением после распаковки

SingleAdwice

(L1) cache
Пользователь
Регистрация
05.08.2020
Сообщения
508
Реакции
448
Депозит
0.0007
Сабж, требуется распаковать пару лямов архивов, чтобы каждый архив распаковался в папку с именем архива и затем исходный архив удалялся
если папка с таким именем уже существует и в ней уже есть файлы, то должна выполнятся перезапись

за решение накину 50$ на пиво
 
Решение
интересует решение задачи стандартными способами используя лишь средства вин
Ок. Powershell. А он же .NET. Заюзаем новомодные фичи, не малварь же пишем под все ОС. Таргет WIN 10, думаю уже все обновились?))
Powershell 5+.
The EnumerateFiles and GetFiles methods differ as follows: When you use EnumerateFiles, you can start enumerating the collection of names before the whole collection is returned; when you use GetFiles, you must wait for the whole array of names to be returned before you can access the array.
EnumerateFiles возвращает итератор. Таким образом можно осуществить lazy перебор файлов, не загружая список всех файлов в RAM. Просто лениво итерируем и на каждую итерацию выполняем...
По итогу помочь никто не смог, проблема ли обьема, или ещё чего либо - хз.

Единственное что нашел, это выход только сначала распаковать всё с полными путями
"E:\7-Zip\7z.exe" x *.zip

после чего отдельной командой удалить все архивы
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Это проблема объема, мой батник работает. Как решить проблему - я тебе так же сказал, просто разбей на куски.
 
По итогу помочь никто не смог, проблема ли обьема, или ещё чего либо - хз.

Единственное что нашел, это выход только сначала распаковать всё с полными путями
"E:\7-Zip\7z.exe" x *.zip

после чего отдельной командой удалить все архивы
Если не решили ещё, я как-то решал похожую задачу, нужно было расшифровать около 10000 файлов, потом распаковать и этого было мало, нужно ещё было объединить это всё в один файл.

Про распаковку, помог такой скрипт, это не моя разработка, просто нашёл в сети, попробуйте...

Это не для 50К. т.к. копипаст:

Код:
@echo off
setlocal enabledelayedexpansion
 
:: папка с архивами
set in=C:\1\2
:: папка с распакованными файлами. Создастся сама.
set out=C:\new
:: папка, в которой находится консольный 7z.exe
set prg=C:\Program Files\7-Zip
 
if /i not exist "%in%" echo Папка с архивами не найдена& pause& exit
 
cd /d "%in%"
for /f "delims=" %%a in ('dir /b /s /a-d *.zip *.rar *.7z') do "%prg%\7z.exe" x "%%a" -o"%out%\%%~nxa"
 
pause
exit

Ну там введите пути...

Если "E:\7-Zip\7z.exe" x *.zip заработало, то батник тоже заработает, т.к. там по сути тоже самое, только автовставка имени для распаковки...

И ещё если файлов очень-очень много, там ограничения в файловой системе какие-то есть, врядли выйдете за них, но могут-быть тормоза, при например заходе в папку и вообще зависания системы...)

Лучше разбить в разные папки вообще тогда распаковку.
 
Если не решили ещё, я как-то решал похожую задачу, нужно было расшифровать около 10000 файлов, потом распаковать и этого было мало, нужно ещё было объединить это всё в один файл.

Про распаковку, помог такой скрипт, это не моя разработка, просто нашёл в сети, попробуйте...

Это не для 50К. т.к. копипаст:

Код:
@echo off
setlocal enabledelayedexpansion

:: папка с архивами
set in=C:\1\2
:: папка с распакованными файлами. Создастся сама.
set out=C:\new
:: папка, в которой находится консольный 7z.exe
set prg=C:\Program Files\7-Zip

if /i not exist "%in%" echo Папка с архивами не найдена& pause& exit

cd /d "%in%"
for /f "delims=" %%a in ('dir /b /s /a-d *.zip *.rar *.7z') do "%prg%\7z.exe" x "%%a" -o"%out%\%%~nxa"

pause
exit

Ну там введите пути...

Если "E:\7-Zip\7z.exe" x *.zip заработало, то батник тоже заработает, т.к. там по сути тоже самое, только автовставка имени для распаковки...

И ещё если файлов очень-очень много, там ограничения в файловой системе какие-то есть, врядли выйдете за них, но могут-быть тормоза, при например заходе в папку и вообще зависания системы...)

Лучше разбить в разные папки вообще тогда распаковку.
Я по сути 70% "E:\7-Zip\7z.exe" x *.zip уже и сделал, так что надобность отпала, попробовал и этот батник, виснет и не подает признаков как и любые другие подобные, тут либо проблема в том как они пытаются сканировать директорию, либо держать сразу в памяти весь список распаковываемых файлов, но факт остается тем же, стандартная команда консольного 7z - работает, если добавлять к ней аргументы и прочее - виснет мёртво, тоже самое и с винраром
 
Я по сути 70% "E:\7-Zip\7z.exe" x *.zip уже и сделал, так что надобность отпала, попробовал и этот батник, виснет и не подает признаков как и любые другие подобные, тут либо проблема в том как они пытаются сканировать директорию, либо держать сразу в памяти весь список распаковываемых файлов, но факт остается тем же, стандартная команда консольного 7z - работает, если добавлять к ней аргументы и прочее - виснет мёртво, тоже самое и с винраром
А если число итераций в цикле ограничить ?

Примерно так:

Код:
@echo off
setlocal enabledelayedexpansion
 
:: папка с архивами
set in=C:\1\2
:: папка с распакованными файлами. Создастся сама.
set out=C:\new
:: папка, в которой находится консольный 7z.exe
set prg=C:\Program Files\7-Zip

set counter=100000
 
if /i not exist "%in%" echo Папка с архивами не найдена& pause& exit
 
cd /d "%in%"
for /f "delims=" %%a in ('dir /b /s /a-d *.zip *.rar *.7z') do (
"%prg%\7z.exe" x "%%a" -o"%out%\%%~nxa"
set counter=%counter%-1
del "%%a"
if %counter% EQU 0 goto brakefor
)
 
:brakefor
echo Continue
pause
exit

Должен распаковать 100000 файлов и удалить архивов...

Я правда не запускал и не тестил могут-быть ошибки в синтаксисе, сейчас в линуксе.
 
А если число итераций в цикле ограничить ?

Примерно так:

Код:
@echo off
setlocal enabledelayedexpansion

:: папка с архивами
set in=C:\1\2
:: папка с распакованными файлами. Создастся сама.
set out=C:\new
:: папка, в которой находится консольный 7z.exe
set prg=C:\Program Files\7-Zip

set counter=100000

if /i not exist "%in%" echo Папка с архивами не найдена& pause& exit

cd /d "%in%"
for /f "delims=" %%a in ('dir /b /s /a-d *.zip *.rar *.7z') do (
"%prg%\7z.exe" x "%%a" -o"%out%\%%~nxa"
set counter=%counter%-1
del "%%a"
if %counter% EQU 0 goto brakefor
)

:brakefor
echo Continue
pause
exit

Должен распаковать 100000 файлов и удалить архивов...

Я правда не запускал и не тестил могут-быть ошибки в синтаксисе, сейчас в линуксе.
Результат тот же, вот кстати обьем с которым он пытается работать
screenshot-9.png
 
Да, похоже он перебирает список файлов, делает что-то типо ls...

Возможно тут батником и не решить, можно для интереса написать что-то на нативном языке, как минимум это решит проблему скана файлов, но лень...)
 
Да, похоже он перебирает список файлов, делает что-то типо ls...

Возможно тут батником и не решить, можно для интереса написать что-то на нативном языке, как минимум это решит проблему скана файлов, но лень...)
Мне уже чисто из интереса хочется узнать чем так принципиально отличаются просто запуск консольного 7z от запуска с аргументами


собственно за решение проблемы для "будущего" поколения, цена та же самая, только посредствами стандартной винды и 7z/winrar
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Смотрите, если файлов слишком много, то точно надо делать не на батнике, попоробуйте JScript/VBScript, либо поставить Python и написать скрипт на нем.
 
Мне уже чисто из интереса хочется узнать чем так принципиально отличаются просто запуск консольного 7z от запуска с аргументами
Ничем...

Смотрите код батника, цикл for...

for /f "delims=" %%a in ('dir /b /s /a-d *.zip *.rar *.7z')

Этот код не только перебирает все файлы в папках, так ещё и пытается по маске выкинуть не нужное ('dir /b /s /a-d *.zip *.rar *.7z').

Ну собственно т.к. файлов много и по факту батник использует не оптимизированные алгоритмы чтения/обработки, то фактически до запуска самого 7zip он не доходит, как-то так вкратце.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
А что у JScript/VBScript более оптимизированно будет ?
Надо посмотреть, но вероятно там можно получить список файлов в директории ни одним большим массивов, а иттеративно получать по одному файлу и распаковывать его в цикле.
 
Python:
import os
import subprocess

ZIP_PATH = "C:\\Program Files\\7-Zip\\7z.exe" # Путь к 7z.exe
DIRECTORY_PATH = "C:\\test\\" # Путь к директории с архивами


if __name__ == "__main__":
    for f in os.listdir(DIRECTORY_PATH):
        full_path = DIRECTORY_PATH + f

        if (f.endswith(".zip")):
            try:
                print(f"[+] Found ZIP file: {f}")
                unpacked_path = full_path + "_unpacked"
                print(f"[+] Processing ZIP file: {f}\n\n")
                subprocess.call(ZIP_PATH + f" x \"{full_path}\" -o\"{unpacked_path}\" -r -aoa" )
                os.remove(full_path)
                print(f"\n\n[+] Created directory and unpacked .zip file: {unpacked_path}")
            except:
                print("[-] Error occured: {f}")
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
* директорию указывать с \\ в конце, как в примере
Ну опять же это не решает проблему миллиона файлов, тк os.listdir возвращает список и он полностью находится в памяти. Нужно использовать что-то типа: https://pypi.org/project/scandir/ (с Питона версии 3.5 эта функция есть в стандартной библиотеке вроде как).
 
Ну опять же это не решает проблему миллиона файлов, тк os.listdir возвращает список и он полностью находится в памяти. Нужно использовать что-то типа: https://pypi.org/project/scandir/ (с Питона версии 3.5 эта функция есть в стандартной библиотеке вроде как).
Python:
import os
import subprocess

ZIP_PATH = "C:\\Program Files\\7-Zip\\7z.exe" # Путь к 7z.exe
DIRECTORY_PATH = "C:\\test\\" # Путь к директории с архивами


if __name__ == "__main__":
    for f in os.scandir(DIRECTORY_PATH):
        full_path = DIRECTORY_PATH + f.name


        if (f.name.endswith(".zip")):
            try:
                print(f"[+] Found ZIP file: {f.name}")
                unpacked_path = full_path + "_unpacked"
                print(f"[+] Processing ZIP file: {f.name}\n\n")
                subprocess.call(ZIP_PATH + f" x \"{full_path}\" -o\"{unpacked_path}\" -r -aoa" )
                os.remove(full_path)
                print(f"\n\n[+] Created directory and unpacked .zip file: {unpacked_path}")
            except Exception as e:
                print(e)
 
тс, пробуй последний вариант, который я скинул в ответ на сообщение DildoFagins
Как я и писал, интересует решение задачи стандартными способами используя лишь средства вин + 7z/winrar

P/s свои архивы я уже распаковал простым консольным 7z с аргументом x + удалил отдельно все архивы
 
интересует решение задачи стандартными способами используя лишь средства вин
Ок. Powershell. А он же .NET. Заюзаем новомодные фичи, не малварь же пишем под все ОС. Таргет WIN 10, думаю уже все обновились?))
Powershell 5+.
The EnumerateFiles and GetFiles methods differ as follows: When you use EnumerateFiles, you can start enumerating the collection of names before the whole collection is returned; when you use GetFiles, you must wait for the whole array of names to be returned before you can access the array.
EnumerateFiles возвращает итератор. Таким образом можно осуществить lazy перебор файлов, не загружая список всех файлов в RAM. Просто лениво итерируем и на каждую итерацию выполняем разархивацию с помощью cmdleta Expand-Archive, не нужно ждать, пока загрузит весь список файлов в память.
1604417806801.png

В то время, как GetFiles возвращает String[], который мы можем проитерировать ТОЛЬКО после получения всех элементов. (То, как сделано у остальных на батниках)
1604417967759.png

Погнали.
Код:
function Extractor {
    param($from, $to, $archiveFormats)
    if($archiveFormats.Length -eq 0) {
        Write-Host 'You must specify archive formats'
        return
    }
    if($(Test-Path $to -PathType Container) -ne $True){
        New-Item $to -itemtype Directory
    }
    if($(Test-Path $from -PathType Container) -eq $True){
        foreach($iter in [System.IO.Directory]::EnumerateFiles($from)){
            Get-ItemProperty $iter | % {
                if($archiveFormats -contains $_.Extension) {
                    $dest = $to + '/' + $_.BaseName;
                    if($(Test-Path $dest)){
                        Remove-Item -Path $dest -Recurse
                    }
                    Expand-Archive -Path $_.FullName -DestinationPath $dest
                    Remove-Item -Path $_.FullName
                }
            }
        }
    }
    else {
        Write-Host "$from folder does not exists";
        return
    }
}

# Вызывать примерно так.
Extractor -from "...\extractorPS\archives" -to "...\extractorPS\result" -archiveFormats @('.zip', '.rar')
Аргументы:
-from "путь к папке с архивами"
-to "путь к папке, где будет разархивированный контент, если папки нет, она создается"
-archiveFormats расширения архивов, которые скрипт возьмет в работу.
Запускать powershell -ep bypass path_to_script.ps1
Потенциально можно улучшить:
1) Добавить потоки
2) Изменить логику проверки Extension файла, поскольку итератор возвращает только путь, но мы форсим создание обьекта FileInfo путем вызова Cmdleta Get-ItemProperty.
 
Последнее редактирование:
Решение


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх