Windows批处理文件将1000个文件分组到字母数字文件夹中

时间:2018-06-12 15:12:13

标签: batch-file

这是我在这里的第一篇帖子/请求。我已经做了很多次搜索,试图找到解决方案,但我认为我的要求有点雄心勃勃。

我使用的是Windows 7,并且更喜欢在 DOS 批处理文件而不是PowerShell中执行此操作。

我的文件夹中包含数以万计的旧Zip压缩文件。由于一个文件夹中有这么多文件,因此列出它们可能会很慢。我想将Zip存档移动到字母表文件夹中,但每个文件夹需要限制为1000个文件

因此,前1000个* .zip文件将被移动到名为A1的文件夹中。将第二千个a * .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 :(得分:2)

这是相同的LotPings' answer,但有一些小修改可以使它更简单,更快:

@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可能会花费太多时间......

答案 1 :(得分:0)

在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
    }
    
  6. 这是未经测试的,但可能会或多或少地起作用。为了进行测试,您应该测试虚拟数据并在-WhatIf上包含Move-Item

    所有这些在cmd中比PowerShell更加痛苦,特别是在分组或数字排序等方面。

答案 2 :(得分:0)

与乔伊相反,我不认为批次是复杂或痛苦的。

在数组中计数需要在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命令仅被回显。

看看计数数组[变量最终输出,看看将创建多少目录。

样品输出短缺:

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

答案 3 :(得分:0)

@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的设置以适合您的具体情况。

您需要更改sourcedirdestdir的设置以适合您的具体情况。

所需的MD命令仅用于ECHO用于测试目的。 在您确认命令正确后,将ECHO MD更改为MD以实际创建目录。

为了测试目的,所需的MOVE命令仅为ECHO在您确认命令正确后,将ECHO MOVE更改为MOVE以实际移动文件。附加>nul以取消报告消息(例如1 file moved

将帖子剪切并粘贴到.BAT文件中。不要因为原因而尝试重新格式化 - 批处理对布局非常敏感。

为了进行测试,我将值dircnt设置为4.您的应用程序需要1000。

我建议您使用一些虚拟目录进行测试。

此例程使用delayed expansion,其中%var%表示解析code block(带括号的语句组)时的变量值,!var!表示可能在其中更改的值/on循环。

第一个问题是在内存中生成目录列表列出的内容按字母顺序排列(/b),基本格式(/a-d)[仅限名称],没有目录名({ {1}})。然后,for命令读取每行代码并分配给%%a

然后使用

c1来包含文件名的第一个字符,该字符与oldc1 - 前一个值进行比较,以便仅在文件名的第一个字符发生更改时重新计算目标目录名。 / p>

如果文件名的第一个字符为数字,则目标子目录将为0?,否则它是c1中文件名的第一个字符。我们将此目录中的filecount初始化为最大值-1,子目录的第二个字符为9

如果目的地已更改,则已建立新目的地

然后我们增加移动的文件数,并通过简单地将文件数除以每个目录的文件来计算目标的第二个字符。由于批处理执行整数数学运算,因此结果为int(每个目录具有此第一个字符/文件的总文件数),因此它从1开始。

然后我们检测目标目录是否已更改,并在需要时创建新目录。

然后移动文件。