Поиск файлов и сортировка по размеру в пакетном файле Windows

У меня есть параметры командной строки для моего пакета script список имен файлов и папки. Для каждого имени файла мне нужно распечатать все подпапки папки, в которой найден файл (путь к этому файлу). Имена подпапок должны быть отсортированы в порядке убывания размеров файлов (файл может иметь разные размеры в разных подпапках).

Я делал это до сих пор, но это не работает:

::verify if the first parameter is the directory

@echo off
REM check the numbers of parameters
if "%2"=="" goto err1
REM check: is first parameter a directory?
if NOT EXIST %1\NUL goto err2
set d=%1
shift
REM iterate the rest of the parameters


for %%i in %dir  do (
find %dir /name %i > temp

if EXIST du /b temp | cut /f 1 goto err3 
 myvar=TYPE temp
echo "file " %i "is in: "

for %%j in %myvar do
 echo %j

 echo after sort
du /b %myvar | sort /nr       
)

:err1
echo Two parameters are necessary 
goto end

:err2
echo First parameter must be a directory.
goto end

:err3 
echo file does not exist.
goto end

:end

Ответ 1

Я не чувствую себя виноватым, отвечая на этот вопрос о домашнем задании сейчас, когда семестр давно прошло. Печатать папки и файлы, рекурсивно используя пакет Windows, является закрытым дублирующимся вопросом, который обсуждает назначение.

Мое первоначальное решение довольно прямолинейно. Есть несколько трюков, чтобы убедиться, что он правильно обрабатывает пути со специальными символами в них, но ничего необычного. Единственный трюк - это оставить размер файла пробелами, чтобы СОРТ работал правильно.

Как и в исходном вопросе, первым параметром должен быть путь к папке (.\works just fine), а последующие аргументы представляют имена файлов (подстановочные знаки в порядке).

@echo off
setlocal disableDelayedExpansion
set tempfile="%temp%\_mysort%random%.txt"

set "root="
for %%F in (%*) do (
  if not defined root (
    pushd %%F || exit /b
    set root=1
  ) else (
    echo(
    echo %%~nxF
    echo --------------------------------------------
    (
      @echo off
      for /f "eol=: delims=" %%A in ('dir /s /b "%%~nxF"') do (
        set "mypath=%%~dpA"
        set "size=            %%~zA"
        setlocal enableDelayedExpansion
        set "size=!size:~-12!"
        echo !size! !mypath!
        endlocal
      )
    ) >%tempfile%
        sort /r %tempfile%
  )
)
if exist %tempfile% del %tempfile%
if defined root popd

Я надеялся избежать создания временного файла, заменив перенаправление и последующий сортировку на прямой, чтобы сортировать. Но это не работает. (см. мой родственный вопрос: Почему задержка с расширением заканчивается, если внутри блока кода с каналами?)

Мое первое решение работает хорошо, за исключением того, что существует возможность дублирования вывода в зависимости от того, какой ввод предоставляется. Я решил, что напишу версию, которая сорвет дубликаты отчетов.

Основная предпосылка была простой - сохранить все выходные данные в один временный файл с именем файла, добавленным в начало отсортированных строк. Затем мне нужно просмотреть результаты и распечатать информацию только после изменения файла и/или пути.

Последний цикл - сложная часть, потому что имена файлов могут содержать специальные символы, такие как ! ^ & и %, которые могут вызывать проблемы в зависимости от того, какой тип расширения используется. Мне нужно установить и сравнить переменные в цикле, что обычно требует отсроченного расширения. Но замедленное расширение вызывает проблемы с расширением переменной FOR при обнаружении !. Я могу избежать задержки расширения, вызывая вне цикла, но тогда переменные FOR становятся недоступными. Я могу передать переменные в качестве аргументов в процедуру CALLed без задержки расширения, но затем я столкнулся с проблемами с % ^ и &. Я могу играть в игры с SETLOCAL/ENDLOCAL, но тогда мне нужно беспокоиться о передаче значений через барьер ENDLOCAL, что требует довольно сложного процесса эвакуации. Проблема становится большим порочным кругом.

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

Я нашел интересное решение, которое использует нечетную особенность переменных FOR.

Обычно область переменных FOR строго находится внутри цикла. Если вы вызываете вне цикла, значения FOR FOR больше не доступны. Но если вы затем выпустите оператор FOR в вызываемой процедуре - переменные вызывающего оператора FOR снова станут видимыми! Проблема решена!

@echo off
setlocal disableDelayedExpansion
set tempfile="%temp%\_mysort%random%.txt"
if exist %tempfile% del %tempfile%

set "root="
(
  for %%F in (%*) do (
    if not defined root (
      pushd %%F || exit /b
      set root=1
    ) else (
      set "file=%%~nxF"
      for /f "eol=: delims=" %%A in ('dir /s /b "%%~nxF"') do (
        set "mypath=%%~dpA"
        set "size=            %%~zA"
        setlocal enableDelayedExpansion
        set "size=!size:~-12!"
        echo(!file!/!size!/!mypath!
        endlocal
      )
    )
  )
)>%tempfile%

set "file="
set "mypath="
for /f "tokens=1-3 eol=/ delims=/" %%A in ('sort /r %tempfile%') do call :proc

if exist %tempfile% del %tempfile%
if defined root popd
exit /b

:proc
  for %%Z in (1) do (
    if "%file%" neq "%%A" (
      set "file=%%A"
      set "mypath="
      echo(
      echo %%A
       echo --------------------------------------------
    )
  )
  for %%Z in (1) do (
    if "%mypath%" neq "%%C" (
      set "mypath=%%C"
      echo %%B %%C
    )
  )
exit /b