【问题标题】:Batch Create Several Folders Based on Multiple Filenames, and Move Multiple Related Files to The Created Folders基于多个文件名批量创建多个文件夹,并将多个相关文件移动到创建的文件夹中
【发布时间】:2020-05-01 00:52:10
【问题描述】:

每周,我的一位同事都必须浏览一个文件夹,其中包含 数百个 已解复用的视频和音频文件,为特定城市电视台单独重命名每个文件,然后将它们分类基于城市名称的文件夹。我创建了一个 .bat 文件来为他重命名它们,现在我想创建一个 .bat 文件,它根据文件名创建新目录,并将相应的文件放入新文件夹中。我复制了一些文件进行测试。

因此,最终结果将是一个包含所有相应文件的“Houston”文件夹、一个包含其文件的“Compton”文件夹、一个“Moline”文件夹等等……每个城市,最多大约 200 个城市,而且我们只会越来越多。

他目前正在搜索“Houston”,剪切所有出现的文件,手动创建一个新文件夹,将其命名为“Houston”并将所有文件粘贴到他的新文件夹中。对于每个城市。 200 次。而且需要几个小时。

文件总是用这个系统命名:X### Random City, ST

凭借我的一点编程知识,我假设脚本可以检测到第一个空格之后和逗号之前的所有字符,复制这些字符(随机城市),创建一个新文件夹,将其命名为复制的字符(随机城市)然后将文件名中包含“随机城市”的任何文件移动到新创建的文件夹中。最终结果就是这样,只是多了很多文件夹。

有没有比我更高级的人可以解释这个问题的最佳方法?

如果我在错误的地方或不够精明,我会提前道歉。干杯!

更新:我搞砸了,了解了标记和分隔符、变量等。这是我所拥有的东西,效果非常好,除了我不确定如何删除末尾的逗号城市名称。我使用空格作为分隔符,如果我理解正确,这会使文本块成为标记,包括我的逗号,使用标记=2。出现的另一个问题;假设有一个城市有两个文本块(令牌),例如。旧金山,巴吞鲁日。我怎么能抓住他们两个,用逗号作为我的停止点?我的代码如下。

@echo off
setlocal enabledelayedexpansion
for %%A in (*.m2v *.mpa) do (
   echo file found  %%A
   for /f "delims=" %%B in ("%%A") do set fname=%%~nB
   for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
   for /f "tokens=2* delims= " %%D in ("!fname!") do set folname=%%D
   echo folder name !folname!
   if not exist "!folname!" (
      echo Folder !folname! doesn't exist, creating
      md "!folname!"
   ) else (
      echo Folder !folname! exists
   )
   echo Moving file %%A to folder !folname!
   move "%%A" "!folname!"
   )
echo Finished

pause

更新 2:我找到了一种解决方法来摆脱逗号,将它添加为分隔符,但我仍然试图围绕 2 个单词的城市来解决问题。我的 Baton Rouge 和 San Fransisco 文件夹分别被命名为“Baton”和“San”。这是我目前的代码,如果我找到更好的方法,我会更新。

setlocal enabledelayedexpansion
for %%A in (*.m2v *.mpa) do (
   echo file found  %%A
   for /f "delims=" %%B in ("%%A") do set fname=%%~nB
   for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
   for /f "delims=," %%B in ("%%A") do set fname=%%~nB
   for /f "tokens=2* delims= " %%D in ("!fname!") do set folname=%%D
   echo folder name !folname!
   if not exist "!folname!" (
      echo Folder !folname! doesn't exist, creating
      md "!folname!"
   ) else (
      echo Folder !folname! exists
   )
   echo Moving file %%A to folder !folname!
   move "%%A" "!folname!"
   )
echo Finished

pause

更新 3

这是我的有效代码。但是,如果文件名前缀/后缀中的字符数发生变化,就会搞砸,您将不得不编辑代码。

@ECHO OFF
SETLOCAL enabledelayedexpansion
FOR %%A in (*.m2v *.mpa) do (
   ECHO file found  %%A
   FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB
   SET folname=!fname:~5,-4!
   ECHO folder name !folname!
   if not exist "!folname!" (
      ECHO Folder !folname! doesn't exist, creating
      MD "!folname!"
   ) else (
      ECHO Folder !folname! exists
   )
   ECHO Moving file %%A to folder !folname!
   MOVE "%%A" "!folname!"
   )
ECHO Finished

PAUSE

使用SET folname=!fname:~5,-4!允许我修剪 M373 前缀、5 个字符和 、TX 后缀、4 个字符,删除逗号并保留城市名称,无论它有多长,或多少个单词它是(例如,佛罗里达州西棕榈滩)。 Antares 在他的回答中提到了这个解决方案,它就像一个魅力。

但这也让我思考

如果前缀中的字符数发生变化(这很可能),我要么必须每次都编辑批处理文件,要么为每种情况创建一个特定的批处理文件。不可怕,但也不是很好。所以我选择了迈克尔希思的答案,它完美无缺。我还不够聪明,无法确切知道原因,但我会剖析它并找出答案。我有很多学习要做。谢谢大家!

【问题讨论】:

  • 这里有问题吗?该站点不是代码编写服务,因此在您尝试完成任务之前,即编写脚本、对其进行测试、调试并在此处发布并提供足够的信息以供我们复制您的问题,您的问题是题外话.完成这些问题后,请随时编辑您的问题。
  • 谢谢。我是新来的,对一般的编码也很陌生,但谢谢你的帮助。我会继续尝试拼凑一些东西,我会把它发回这里,这是不可避免的问题!
  • 我希望没有逗号的文件名是拼写错误,因为您的重命名脚本肯定不会导致命名不一致。
  • Dallas, TXDallas, GA 的文件会进入同一个 Dallas 目录吗?
  • 是的。而且我认为这很混乱,但这就是我们命名文件夹的方式,因为显然我们还没有遇到来自同名城市的 2 个不同站点的问题。显然。然而,这是一个问题,我会写一个留下逗号和后缀的,因为我相信我们会在某个时候需要它。

标签: windows batch-file cmd


【解决方案1】:
@echo off
setlocal

rem A=Fullpath, B=Name before comma, C=B prefix, D=B without prefix.
for /f "delims=" %%A in ('dir /b *.m2v *.mpa') do (
    for /f "delims=," %%B in ("%%~nA") do (
        for /f "tokens=1,*" %%C in ("%%~B") do (

            if not exist "%%~D\" (
                echo Folder "%%~D" doesn't exist, creating
                md "%%~D"
            )

            if exist "%%~D\" (
                echo Moving file "%%~A" to folder "%%~D\"
                move /y "%%~A" "%%~D\"
            ) else echo Folder "%%~D\" doesn't exist
        )
    )
)

echo Finished
pause

3 for 循环获取所需的令牌。

  • 1st 将获得完整路径。
  • 第二次获取逗号前的名称。
  • 第三次得到不带前缀的名字。

在嵌套的for 循环中检查文件夹是否存在,如果不存在则创建它。然后如果文件夹存在,将文件移动到里面。

【讨论】:

    【解决方案2】:

    这是我的有效代码。但是,如果文件名前缀/后缀中的字符数发生变化,就会搞砸,您将不得不编辑代码。

    @ECHO OFF
    SETLOCAL enabledelayedexpansion
    FOR %%A in (*.m2v *.mpa) do (
       ECHO file found  %%A
       FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB
       SET folname=!fname:~5,-4!
       ECHO folder name !folname!
       if not exist "!folname!" (
          ECHO Folder !folname! doesn't exist, creating
          MD "!folname!"
       ) else (
          ECHO Folder !folname! exists
       )
       ECHO Moving file %%A to folder !folname!
       MOVE "%%A" "!folname!"
       )
    ECHO Finished
    
    PAUSE
    
    

    使用SET folname=!fname:~5,-4!允许我修剪 M373 前缀、5 个字符和 、TX 后缀、4 个字符,删除逗号并保留城市名称,无论它有多长,或多少个单词它是(例如,佛罗里达州西棕榈滩)。 Antares 在他的回答中提到了这个解决方案,它就像一个魅力。

    但这也让我思考

    如果前缀中的字符数发生变化(这很可能),我要么必须每次都编辑批处理文件,要么为每种情况创建一个特定的批处理文件。不可怕,但也不是很好。所以我选择了迈克尔希思的答案,它完美无缺。我还不够聪明,无法确切知道原因,但我会剖析它并找出答案。我有很多学习要做。谢谢大家! Before Image After Image

    【讨论】:

    • 我认为你可以用set fname=%%~nA 简化FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB,因为FOR 只会产生%%A 的完整字符串。
    • 正如我所提到的,整个 if not exist (...) else (... ) 块可以只替换为 MD "!folname!" ;) 除非您认为自定义输出对您的情况很重要,然后没关系!
    【解决方案3】:

    我可以给你一些提示。如果您有更具体的问题案例,请随时再次更新您的问题。

    您可以像这样剪切字符串的最后 X 个字符:%variablename:~0,-X%

    如果您知道城市部分的变量,例如%%D 和 %%E 什么的,你可以像md "%%D %%E" 一样再次连接它们。但是,这仅适用于固定数量的令牌,就像这里的两个一样。
    如果您需要 for 循环之外的结果,您可以将此连接存储在自己的变量中。例如使用set myVariable=%%D %%E,并使用%myVariable%!myVariable!(需要延迟扩展时)显示它,例如md "%myVariable%"

    一个很好的解决方法:如果只考虑少数“特殊城市”,那么您可以在脚本末尾添加一些重命名命令,例如rename San "San Francisco"rename Baton "Baton Rouge" 等。如果有更多的“San”城市(例如“San Bernadino”),将无法正常工作,因为这无法再区分了。但在这种情况下,复制到单独的文件夹也已经失败了。

    在您的脚本中,您检查现有文件夹。我认为你可以省略它。 mdmkdir 要么创建该目录,要么不执行任何操作(如果存在)。好吧,他们确实向控制台打印了一条消息,可以忽略。如果您不想看到它们,请将错误消息流重定向到nul,例如md myFolder 2>nul。这将吞噬任何错误消息,但您不太可能收到除您的场景之外的任何其他错误消息。

    您可以像这样简化您的方法:我认为您的文件重命名效果很好。您的“复制”脚本可能只是一个明确说明的命令列表(如果要考虑新城市,也可以很快编辑)。
    像这样使用每个城市的条目设置批处理文件:

    @echo off
    mkdir "Moline"
    copy "*Moline*.*" "Moline"
    
    mkdir "San Francisco"
    copy "*San Francisco*.*" "San Francisco"
    
    ...
    
    echo done.
    

    副作用是,将创建每个城市的文件夹,而不仅仅是文件复制到的文件夹。无论如何,它可能适合您的需求。

    另外,我想给你一些帮助/文档来源的指针:

    在命令行上,您可以通过执行批处理文件中使用的命令并附加/? 来获得广泛的帮助。例如,set /? 为您提供了很多有用的东西,您可以使用变量/字符串操作。
    试试:for /?if /?(例如errorlevel),也许是call /?goto /?等。

    所有命令行命令的一个很好的来源是https://www.ss64.com。该站点甚至为 PowerShell 和 Linux Bash 等提供了广泛的帮助。在这种情况下与您相关的是CMD,直接链接:https://ss64.com/nt/

    编辑/更新:
    查看 set 命令上的符号替换以构建类似

    的内容
    if "%myVariable:~0,1%" == "M" (set myvariable=%myvariable:~1%)
    

    第一部分“剪切”第一个字符并检查它是否为 M,如果是,则保留除第一个字符之外的所有内容。有了这个,您可以使您的文件名均匀,以便在您的批处理文件中处理它们。
    您还可以使用%myVariable:, TX=%“删除”一个字母或子字符串,例如,它将任何“,TX”替换为“无”。

    哦,这也有助于删除文件名中的任何空格。然后你可以提取没有空格问题的“SanFrancisco”;)虽然文件夹名称没有空格。这可以在最后通过进一步的rename 命令解决。

    【讨论】:

    • 非常感谢!由于我们的文件命名系统没有改变,我最终使用了您所说的“剪切/增长”来剪切/增长变量字符串中的字符。
    • 其实……我没有想到这个。并非所有程序都是“M”。这意味着这个批处理文件只能使用 4 个字符的前缀,例如。 M123 Random City, TX.m2v 这是一个问题,因为我们还有 123 Random City, TX.m2v 的文件。看起来迈克尔希思也有一个很好的答案。
    • 在答案底部查看我的编辑/更新。
    【解决方案4】:

    这是另一种方法,只是为了多样化

    @Echo Off
    SetLocal DisableDelayedExpansion
    
    Set "SourceDir=.\Batch Rename\BACKUP"
    
    If Exist "%SourceDir%\" For /F "EOL=|Delims=" %%G In (
        '%__AppDir__%where.exe "%SourceDir%":"*, ??.m??" 2^>NUL'
    )Do (Set "FileBaseName=%%~nG"&SetLocal EnableDelayedExpansion
        For /F "EOL=|Delims=," %%H In ("!FileBaseName:* =!")Do (EndLocal
        %__AppDir__%Robocopy.exe "%%~dpG." "%%~dpG%%H" "%%~nxG" /Mov>NUL))
    

    此方法使用where 命令过滤您的文件。它仅选择以逗号结尾、后跟空格、后跟两个字母、后跟以字符m 开头的三个字母扩展名的文件名进行移动。它使用robocopy 命令移动文件,如果它们不存在,则自动创建目标目录。它只使用了两个for 循环,第二个循环将第一个空格和下一个逗号之间的字符串隔离开来。

    我已经这样做了,以便脚本可以位于任何地方,不一定在文件所在的目录中。这个位置是在脚本的3 行设置的,如果你想修改它,请确保你的位置保持在= 和结束双引号" 之间,并且不以尾随结尾-斜线,\。它当前设置为相对目录,(基于您的屏幕截图中可见的目录),但您显然也可以使用绝对路径,例如Set "SourceDir=C:\Users\UserName\Videos"。如果您希望将脚本与要移动的文件保持在同一目录中,请将其更改为 Set "SourceDir=.",然后双击运行即可。

    【讨论】:

      【解决方案5】:

      当我不得不为手头的任务开发脚本时,我可能会实施一些安全功能,以免移动错误的文件。您的示例数据显示成对的 .m2v.mpa 文件,但我可能不会认为这是理所当然的。我也不会依赖固定长度的前缀。最后,我可能还要考虑litcomment

      这是我的尝试(请参阅代码中的所有解释性rem-remarks):

      @echo off
      setlocal EnableExtensions DisableDelayedExpansion
      
      rem // Define constants here:
      set "_ROOT=%~dp0."         & rem // (root directory containing the files to be processed)
      set "_MASK=M??? *, ??.m2v" & rem // (mask to find the files to be processed)
      set _EXTS=".m2v" ".mpa"    & rem /* (list of extensions that must all be present;
                                   rem     extensions are not checked if this is empty;
                                   rem     `_MASK` should then be changed to end with `.m*`) */
      set "_FILT=^M[0-9][0-9][0-9] [^,][^,]*, [A-Z][A-Z]\.[^\.][^\.]*$"
                                   rem // (additional filter to find files; deactivate by `.*`)
      set "_SEPS=,"              & rem /* (defines (a) separator character(s) to derive the
                                   rem     sub-directory; leave it blank to use full name) */
      
      rem // Change into the root directory:
      pushd "%_ROOT%" && (
          rem /* Loop through all files matching the mask as well as the additional filter;
          rem    if the post-filtering is not needed, remove `^|` and everything behind: */
          for /F "delims= eol=|" %%F in ('
              dir /B /A:-D-H-S "%_MASK%" ^| findstr /I /R /C:"%_FILT%"
          ') do (
              rem // Get the portion in front of the `,` of the file name:
              for /F "delims=%_SEPS% eol=|" %%G in ("%%~nF") do (
                  rem // Split that portion at the first space to get the city name:
                  for /F "tokens=1* eol=|" %%H in ("%%G") do (
                      rem // initialise flag that indicates whether to move the current file:
                      set "FLAG=#"
                      rem // Skip the following checks if there are no extensions defined:
                      if defined _EXTS (
                          rem // Loop through the extensions in the list:
                          for %%E in (%_EXTS%) do (
                              rem /* Reset flag if file with current name and iterated extension
                              rem    cannot be found; this ensures that files with all listed
                              rem    extensions do exist, otherwise no files are moved: */
                              if not exist "%%~nF%%~E" set "FLAG="
                              rem /* Reset flag if file with current name and iterated extension
                              rem    is actually a directory (though this is very unlikely): */
                              rem if exist "%%~nF%%~E\*" set "FLAG="
                              rem /* Reset flag if file with current name and iterated extension
                              rem    is already located in the target sub-directory: */
                              if exist "%%I\%%~nF%%~E" set "FLAG="
                          )
                      )
                      rem // Do the following steps only if the flag has not been reset:
                      if defined FLAG (
                          rem // Create target sub-directory (suppress potential error message):
                          2> nul md "%%I"
                          rem // Check if there are dedicated extensions defined:
                          if defined _EXTS (
                              rem // Loop through the extensions in the list again:
                              for %%E in (%_EXTS%) do (
                                  rem /* Move file with current name and iterated extension;
                                  rem    nothing is overwritten due to the preceding checks: */
                                  > nul move "%%~nF%%~E" "%%I\%%~nF%%~E"
                              )
                          ) else (
                              rem /* Empty list of extensions, hence just move the current file;
                              rem    if you do want to overwrite, remove the `if exist´ part: */
                              if not exist "%%I\%%F" > nul move /Y "%%F" "%%I\%%F"
                          )
                      )
                  )
              )
          )
          rem // Return from root directory:
          popd
      )
      
      endlocal
      exit /B
      

      脚本顶部Define constants here: 部分中的值是为适合您的示例数据而定义的,但它们可以很容易地在那里调整以在一个地方配置脚本:

      • _ROOT:指向你的输入文件所在的目录; %~dp0. 指向脚本的父目录,当然你也可以在这里指定任何其他的绝对目录路径;
      • _MASK:是一种文件模式,每对匹配一个文件(仅.m2v文件,其他被_EXTS覆盖); M??? 匹配四个字符的前缀,但您可以将其更改为 M?*,例如,也可以匹配 M1M9999 等前缀;但是,如果您这样做,也要相应地编辑 _FILT
      • _EXTS:定义所有必须存在的扩展列表;这意味着对于某个基本文件名(如M372 Houston, TX,每个给定扩展名都必须存在一个文件,因此在我们的情况下M372 Houston, TX.m2vM372 Houston, TX.mpa,否则这些文件不会被移动;如果你不关心这样的一对是否完整,只需声明set "_EXTS="(所以清除它)并将_MASK的扩展名从.m2v更改为.m*,因此所有扩展名以.m开头的文件都会被移动;
      • _FILT:构成文件名的附加过滤器,以排除错误文件;这目前也反映了一个四字符前缀,但如果并非总是如此,只需将M[0-9][0-9][0-9] 更改为M[0-9]*;如果您不想过滤,请将其设置为.*,以便匹配所有内容;
      • _SEPS:定义用于拆分基本文件名的字符以派生相应的子目录,因此在该字符之前结束并在第一个 SPACE 之后开始的所有内容都是生成的子目录目录名称;如果您没有在此处定义字符,则将采用整个剩余的基本文件名(因此第一个 SPACE 之后直到但不包括(最后一个). 的所有内容);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-23
        相关资源
        最近更新 更多