Пакетный файл Windows для группировки 1000 файлов в алфавитно-цифровые папки - PullRequest
0 голосов
/ 12 июня 2018

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

Я использую Windows 7 и предпочел бы сделать это в DOS пакетный файл, а не PowerShell.

У меня есть папки, содержащие десятки тысяч старых Zip-архивов.Поскольку в одной папке так много файлов, их список может быть медленным.Я хочу переместить архивы Zip в алфавитные папки, но каждая папка должна быть ограничена 1000 файлами

Поэтому первые 1000 файлов a * .zip будут перемещены в папку с именем A1.Вторая тысяча * .zip файлов в папку с именем A2 и т. Д.

Файлы должны быть перемещены по порядку, так что если последний файл, скопированный в A1, будет an_example_file_97.zip, то первыйфайл, перемещенный в папку A2, будет an_example_file_98.zip

Мне нужно сделать это для всего алфавита, а также для Zip-архивов с числовым именем.Тогда я получу такую ​​структуру папок / файлов, как эта ...

<DIR> 01
    1000 zip archives whose filename begins with a number
<DIR> 02
    Next 1000 zip archives whose filename begins with a number
<DIR> 03
    Next 1000 zip archives whose filename begins with a number

<DIR> A1
    1000 zip archives whose filename begins with A
<DIR> A2
    Next 1000 zip archives whose filename begins with A
<DIR> A3
    Next 1000 zip archives whose filename begins with A
<DIR> B1
    1000 zip archives whose filename begins with B
<DIR> B2
    Next 1000 zip archives whose filename begins with B
<DIR> B3
    Next 1000 zip archives whose filename begins with B

<DIR> Z1
    1000 zip archives whose filename begins with Z
<DIR> Z2
    Next 1000 zip archives whose filename begins with Z
<DIR> Z3
    Next 1000 zip archives whose filename begins with Z

Приношу свои извинения, если это решение уже существует на этом сайте, но сложно точно знать, что искать.

Спасибо.

Ответы [ 4 ]

0 голосов
/ 12 июня 2018

Это тот же самый ответ LotPings , но с парой небольших модификаций, которые делают его проще и быстрее:

@Echo off
SetLocal EnableExtensions EnableDelayedExpansion

PushD "X:\start\here"
for %%A in (*.*) Do (
   Set "Name=%%~nA"
   Set "N=!Name:~0,1!"
   If "!N!" lss "a" Set "N=0"
   Set /A "Array[!N!]+=1, F=Array[!N!] / 1000 +1"
   Set "Dest=!N!!F!"
   If not Exist "!Dest!" REM MD "!Dest!"
   Echo Move "%%A" "!Dest!\"
)

Если список простой команды for %%A не указанотсортировать, затем изменить строку for %%A in (*.*) Do ( на эти две строки:

for %%L in (0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) Do (
   for /F "Delims=" %%A in ('Dir /B /ON %%L*') Do (

... и добавить закрывающие скобки в конце.Это лучше, чем простой for /F "Delims=" %%A in ('Dir /B /ON *') Do (, потому что если количество файлов огромно, то выполнение такого for может занять слишком много времени ...

0 голосов
/ 12 июня 2018

В отличие от Джои, я не думаю, что это сложно или болезненно для партии.

Для подсчета в массиве требуется два уровня задержки с расширением внутри цикла for, но это так.

@Echo off&SetLocal EnableExtensions EnableDelayedExpansion
PushD "X:\start\here"
for /F "Delims=" %%A in ('Dir /B /ON *') Do (
  Set "Name=%%~nA"
  Set "N=!Name:~0,1!"
  Echo !N!|Findstr "[0-9]" 2>&1>NUL &&Set N=0
  Set /A "Array[!N!]+=1"
  Call Set "Count=%%Array[!N!]%%"
  Set /A "F=!Count! / 1000 +1, I = !Count! %% 1000"
  Set "Dest=!N!!F!"
  If not Exist "!Dest!" REM MD "!Dest!"
  Echo Move "%%A" "!Dest!\"
)
Set Array[
Pause
Popd

Во время тестирования команда MD удалена,
Команда Move только отображается.

Посмотрите на счетную матрицу [переменная наконец выводит, чтобы увидеть, сколько будет создано dir.

Вывод закороченного примера:

Move "WSH_Shell_4.html" "W1\"
Move "xyz" "x1\"
Move "yarn.bat" "y1\"
Move "ZehnerZaehlen.cmd" "Z1\"
Move "zeiten.txt" "z1\"
Array[0]=7
Array[A]=10
Array[B]=3
Array[c]=30
Array[D]=19
Array[E]=15
Array[F]=18
Array[G]=29
Array[H]=11
Array[I]=9
Array[J]=2
Array[K]=1
Array[L]=9
Array[m]=20
Array[N]=5
Array[O]=4
Array[P]=11
Array[r]=16
Array[S]=25
Array[T]=17
Array[U]=19
Array[v]=3
Array[W]=19
Array[x]=1
Array[y]=1
Array[Z]=2
0 голосов
/ 12 июня 2018
@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir"
SET "oldc1=*"
SET "oldsd=*"
SET "dircnt=4"

FOR /f "delims=" %%a IN (
 'dir /b /a-d /on "%sourcedir%\*.zip" '
 ) DO (
 SET "c1=%%a"
 SET "c1=!c1:~0,1!"
 IF /i "!c1!" neq "!oldc1!" (
  REM we changed initial character
  SET "oldc1=!c1!"
  SET "destsd=!c1!"
  FOR %%b IN (0 1 2 3 4 5 6 7 8 9) DO IF "%%b"=="!c1!" SET "destsd=0"
  IF /i "!oldsd!" neq "!destsd!" (
   SET /a fcount=dircnt-1
   SET /a olddestsd2=9
   SET "oldsd=!destsd!"
  )
 )
 SET /a fcount +=1
 SET /a destsd2=fcount / dircnt
 IF !destsd2! neq !olddestsd2! (
  SET /a olddestsd2=destsd2
  ECHO MD "%destdir%\!destsd!!destsd2!" 2>nul
 )
 ECHO MOVE "%sourcedir%\%%a" "%destdir%\!destsd!!destsd2!\"
)

GOTO :EOF

Вам потребуется изменить настройку sourcedir в соответствии с вашими обстоятельствами.

Вам потребуется изменить настройки sourcedir и destdir в соответствии с вашими обстоятельствами.

Требуемые MD-команды просто ECHO отредактированы для целей тестирования. После того как вы убедились, что команды верны , измените ECHO MD на MD, чтобы фактически создать каталоги.

Необходимые команды MOVE просто ECHO ed для целей тестирования, После того как вы убедились, что команды верны , измените ECHO MOVE на MOVE, чтобы фактически переместить файлы.Добавьте >nul для подавления сообщений отчета (например, 1 file moved)

Вырежьте и вставьте сообщение в файл .BAT.Не пытайтесь переформатировать по эстетическим причинам - пакет может быть очень чувствителен к макету.

Для тестирования я установил значение dircnt в 4. Для вашего приложения требуется 1000.

Я быПредлагаем вам использовать несколько фиктивных каталогов для тестирования.

Эта подпрограмма использует delayed expansion, где %var% представляет значение переменной, когда code block (группа операторов в скобках) анализируется, а !var! значение какэто может измениться в цикле.

Первая проблема заключается в создании списка каталогов в памяти. Этот список отсортирован в алфавитном порядке (/on) и в основном формате (/b) [только имена] без имен каталогов (/a-d).Каждая строка строк затем читается командой for и присваивается %%a.

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

Если первый символ имени файла числовой, тогда подкаталог назначения будет 0?, в противном случае это первый символимя файла теперь в c1.Мы инициализируем количество файлов в этом каталоге максимально - 1, а второй символ подкаталога - 9.

Это установило новое назначение , если назначение изменилось.

Затем мы увеличиваем количество перемещаемых файлов и вычисляем второй символ места назначения, просто разделив число файлов на файлы на каталог.Так как пакет выполняет целочисленную математику, результатом является int (общее количество файлов с этим первым символом / файлов на каталог), поэтому он начинается с 1.

Затем мы определяем, изменился ли каталог назначения, и создаемновый каталог, если требуется.

Затем переместите файл.

0 голосов
/ 12 июня 2018

В PowerShell я, вероятно, подхожу к этому следующим образом (пока не особо заботясь об эффективности):

  1. Получить список файлов:

    Get-ChildItem |
    
  2. Сортировать их по имени (с номерами в имени, отсортированными по номерам, а не по алфавиту)

    Sort-Object {
      [regex]::Replace($_.Name, '\d+', { '{0:0000000000}' -f ([int]$args[0].Value) })
    }
    

    Это позаботится о сортировке some-name-100 после some-name-99.

  3. Группировка по первому символу:

    Group-Object {
      $first = $_.Name[0];
      # Emit only a 0 for all numbers
      if ($first -match '[0-9]') { '0' } else { $first }
    } |
    
  4. Возьмите эти группы и разделите их на партии по не более 1000 элементов

    ForEach-Object {
      $name = $_.Name
      $group = $_.Group
      0..([Math]::Floor($_.Count / 1000)) |
        ForEach-Object {
          $items = $group[($_ * 1000)..($_ * 1000 + 999)
          [pscustomobject] @{
            FirstLetter = $name
            Index = $_ + 1
            Items = $items
          }
        }
    } |
    
  5. Создание папок и перемещение файлов в соответствующие папки

    ForEach-Object {
      $dir = New-Item Directory ($_.FirstLetter + $_.Index)
      $_.Items | Move-Item -Destination $dir
    }
    

Это не проверено, но, вероятно, должно работать более или менее.Для тестирования вам, возможно, следует проверить фиктивные данные и включить -WhatIf в Move-Item.

Все это будет гораздо более болезненным в cmd, чем в PowerShell, особенно такие вещи, как группировка или сортировкачисленно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...