1

В следующем коде мне нужно взять путь и размер папки и подпапок в файл. Но когда цикл запускается во второй раз, путь и размер не печатаются в файл. size.txt содержит только путь и размер первой папки. Пожалуйста, кто-нибудь, помогите мне.

@echo off

SETLOCAL EnableDelayedExpansion
SET xsummary=
SET xsize=

for /f "tokens=1,2 delims=C" %%i IN ('"dir /s /-c /a | find "Directory""') do (echo    C%%j >> abcd.txt)
for /f "tokens=*" %%q IN (abcd.txt) do (
cd "%%q"
For /F "tokens=*" %%h IN ('"dir /s /-c /a | find "bytes" | find /v "free""') do Set    xsummary=%%h
For /f "tokens=1,2 delims=)" %%a in ("!xsummary!") do set xsize=%%b
Set xsize=!xsize:bytes=!
Set xsize=!xsize: =!
echo.%%q >> size.txt
echo.!xsize! >> size.txt

)

2 ответа2

1

Если вы можете справиться с этим, избегайте этой путаницы, взяв утилиту Sysinternals du и запустив du -v <directoryname> . Вывод будет килобайт. Опция -v означает вывод размера всех подкаталогов.


В вашем текущем сценарии проблема заключается в добавлении текста в цикле. Простой обходной путь - вывести в окно командной строки, вот так:

echo %%q
echo !xsize!

и когда вы запускаете пакетный файл, отправляете его вывод в текстовый файл: test.bat > size.txt


Быстрый совет: первый бит можно упростить с помощью:

for /f "tokens=*" %%q in ('dir /a:d /s /b') do (
    cd "%%q"
    ...
    echo !xsize!
)

По сути, он заменяет использование find параметром /a:d (только для списков каталогов) и /b (использовать пустой формат, без заголовка или сводки). Это также избавляет от временного файла.


Модифицированная версия для работы с папками с восклицательными знаками в названии. Используя ответ Скотта. Смотрите комментарии ниже.

Обратите внимание, что REM означает, что это комментарий.

@echo off

REM clear the file
type NUL > size.txt

for /f "tokens=*" %%q IN ('dir /a:d /s /b') do (
    echo %%q >> size.txt
    REM Run :getsize, passing "%%q" including quotes as the first parameter
    call :getsize "%%q" >> size.txt
)

REM go to the end of the file, i.e. exit
goto :eof

:getsize
REM %1 is the  first parameter. It should already be quoted.
for /f "tokens=*" %%h IN ('"dir /s /-c /a %1 | find "bytes" | find /v "free""') do set xsummary=%%h
for /f "tokens=1,2 delims=)" %%a in ("%xsummary%") do set xsize=%%b
set xsize=%xsize:bytes=%
set xsize=%xsize: =%
echo %xsize%

goto :eof

Альтернативой является использование PowerShell:

Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | %{$_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0; $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum; $_} | Sort-Object DirSize -Descending | Select-Object -First 10 FullName,DirSize | Format-Table -AutoSize | Out-String -Width 4096 | Out-File 'sizes.txt';

Это слишком сложно для того, чтобы быть как можно более полным - также большинство этих команд имеют более короткие псевдонимы; это просто более читабельно. И если вы разделите определенные разделы на функции / их собственную строку, это станет намного более читабельным и, возможно, короче.

Расширяя это:

  • Get-ChildItem -Recurse

    Рекурсивный список всех подкаталогов и файлов

  • ?{ $_.PSIsContainer }

    Фильтр, поэтому сохраняются только каталоги (? является псевдонимом для Where-Object). $_ ссылается на объект (ы), переданный по этому каналу - поскольку Get-ChildItem передает список / массив объектов, он обрабатывает их по одному за раз.

  • %{$_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0; $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum; $_}

    Рассчитать размер каждого каталога (% является псевдонимом для Foreach-Object , поэтому запустите то, что находится между { } для каждого отдельного объекта. Каждый объект является каталогом, как в предыдущих частях.)

    Расширяя это:

    • $_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0;

      Точка с запятой (;) обозначает конец строки / оператора и не передает выходные данные, в отличие от канала (|). Этот конкретный бит не имеет вывода, и он просто добавляет свойство DirSize к объекту каталога и инициализирует его в 0. Это сделано для более легкой фильтрации и более аккуратного вывода позже.

    • $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum;

      Установите DirSize в свойство Sum выходных данных этой команды. $() означает выполнить то, что находится внутри скобок, и заменить это на.

      Расширяя это:

      • Get-ChildItem -Recurse $_.Fullname

        Получить все содержимое текущего объекта каталога ($_ , Fullname просто означает полный путь). -Recurse означает также подсчитывать содержимое подкаталогов.

      • ?{ -not $_.PSIsContainer }

        Фильтруйте так, чтобы сохранялись только файлы. Каталоги имеют свойство Length равное 0, поэтому нам нужно использовать sum для файлов внутри - по сути, то, что делает ваш пакетный скрипт, за исключением того, что мы имеем дело с реальными числами, объектами и свойствами, вместо того, чтобы захватывать «число» как текст с помощью find .

      • Measure-Object -Sum -Property Length

        Вычислить сумму всех переданных ему свойств Length . Это возвращает объект, содержащий свойство Sum , которое использовалось выше (.Sum)

    • $_

    По сути, echo объект, чтобы передать его следующему разделу. Теперь у него есть свойство DirSize .

  • Sort-Object DirSize -Descending

    Сортировать по убыванию по DirSize

  • Select-Object -First 10 FullName,DirSize

    Выведите только первые 10, и только имя и размер

  • Format-Table -AutoSize | Out-String -Width 4096 | Out-File 'sizes.txt';

    Вывод в sizes.txt . Биты Format-Table и Out-String предназначены для предотвращения усечения до ширины окна консоли (обычно 80 символов). 4096 должно быть более чем достаточно символов для любого пути Windows и размера каталога, учитывая, что путь Windows обычно ограничен 255 символами (IIRC). AutoSize означает просто сделать его как можно более коротким, без усечения, вместо заполнения большим количеством пробелов для размещения 4096 символов.

И команда в виде однострочного запуска в cmd.exe (вы можете запустить вышеуказанную версию в PowerShell):

powershell -c "Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | %{$_ | Add-Member -MemberType NoteProperty -Name DirSize -Value 0; $_.DirSize = $(Get-ChildItem -Recurse $_.Fullname | ?{ -not $_.PSIsContainer } | Measure-Object -Sum -Property Length).Sum; $_} | Sort-Object DirSize -Descending | Select-Object -First 10 FullName,DirSize | Format-Table -AutoSize | Out-String -Width 4096 | Out-File 'sizes.txt';"
1

Проблема в cd - вы создаете двухстрочный файл size.txt в каждом каталоге.  Пытаться

for ... %%h in ('"dir /s /-c /a  %%q  | find "bytes" | find /v "free""') ...

и удалите cd .

Кстати, вы понимаете, что вы получаете совокупные итоги?  Например, если ваша структура каталогов выглядит так

a   100K
a\b 200K
a\c 400K

тогда ваш результат скажет

a
700000
a\b
200000
a\c
400000

Это потому, что второй dir имеет параметр /s .  Хорошо, если ты этого хочешь; Я просто подумал, что это мог быть недосмотр.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .