批处理:从文件名(子字符串)创建文件夹

Posted

技术标签:

【中文标题】批处理:从文件名(子字符串)创建文件夹【英文标题】:Batch: Create folders from filename (substring) 【发布时间】:2021-05-03 17:16:07 【问题描述】:

我有很多我想以不同方式组织的文件。批处理脚本应使用文件名中日期左侧的子字符串创建文件夹。

文件现在这样命名:

This_is_my_file_21.01.29_22-00_abc_115.avi
This_is_my_file_20.09.29_21-10_abc_15.avi
This_is_another_file_21.01.29_22-00_abc_55.avi

模式:

____.avi

文件夹应该这样命名:

This_is_my_file This_is_another_file

问题是,如何为我的文件夹名称获取正确的子字符串?

这是我目前所拥有的:

@echo off
setlocal

set "basename=."
for /F "tokens=1* delims=." %%a in ('dir *.avi /B /A-D ^| sort /R') do (
   set "filename=%%a"
   setlocal EnableDelayedExpansion
   

   
   for /F "delims=" %%c in ("!basename!") do if "!filename:%%c=!" equ "!filename!" (
      set "basename=!filename!"
      md "!basename:~0,-23!"
   )
   move "!filename!.%%b" "!basename:~0,-23!"
   for /F "delims=" %%c in ("!basename!") do (
      endlocal
      set "basename=%%c
   )
)

【问题讨论】:

所以所有文件名的格式都是*??.??.??-*.aviyes? 看起来你很亲密。 %%a 的值为This_is_my_file_21%%b 的值为01.29_22-00_abc_115.avi。您将文件名的第一部分分配给了一个变量,现在您所要做的就是使用 -3 作为子字符串,因为您想删除这两个数字和下划线。 set "filename=!filename:~0,-3!"。现在变量filename 的值是This_is_my_file @jwdonahue 是的,所有文件的名称中都有 _??.??.??_.avi @ Squashman 我以前试过这个,但最后我得到的值是 !filename:~0,-3!在我的变量中。好像我快到了,但出了点问题。 【参考方案1】:
@ECHO OFF
SETLOCAL
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.

SET "sourcedir=u:\your files\t w o"

FOR /f "delims=" %%b IN ('dir /b /a-d "%sourcedir%\*.avi" ' ) DO (
 SETLOCAL ENABLEDELAYEDEXPANSION
  CALL :countus "%%b"
  IF DEFINED subdir (
   MD "!subdir!" 2>NUL
   ECHO MOVE "%sourcedir%\%%b" "%sourcedir%\!subdir!\"
  ) ELSE (
   ECHO Failed pattern check %%b
  )
 ENDLOCAL
)
GOTO :EOF

:: count number of underscores before pattern YY.MM.DD_hh-mm
:countus
SET /a ucount=0
:countusloop
SET /a ucount+=1
SET /a scount=ucount+1
FOR /f "tokens=%ucount%,%scount%delims=_" %%q IN ("%~1") DO SET "str1=%%q"&SET "str2=%%r"
IF NOT DEFINED str2 SET "subdir="&GOTO :EOF 
:: is %str1%.%str2:-=.%. of form np.np.np.np.np where np is a number-pair?
SET "candidate=%str1%.%str2:-=.%."
FOR /L %%c IN (10,1,99) DO IF DEFINED candidate SET "candidate=!candidate:%%c.=!"&IF NOT DEFINED candidate GOTO success
FOR /L %%c IN (0,1,9) DO IF DEFINED candidate SET "candidate=!candidate:0%%c.=!"&IF NOT DEFINED candidate GOTO success
GOTO countusloop
:success
SET "subdir=%~1"
FOR /f "delims=:" %%e IN ("!subdir:_%str1%_%str2%=:!") DO SET "subdir=%%e"
GOTO :eof

“移动”命令只是为了验证而回显。从echo move 中删除echo 以实际移动文件。

【讨论】:

谢谢 :) 看起来真的很好。等我回家后我会试试的。【参考方案2】:

这种可能的解决方案使用了这样一个事实,即如果您向后工作,您的文件名具有已知数量的下划线。我所做的只是用反斜杠替换那些下划线,显然不能包含在文件名中。然后我可以使用相对路径来提升文件名,就好像它是一个目录树一样,直到我只剩下日期序列之前的部分,然后我再次用下划线替换反斜杠。我将结果与robocopy 一起使用,它有一个移动选项,如果目标目录不存在,它将自动创建它。一开始,我使用where.exe在与批处理文件相同的目录中执行目录搜索(如果你想使用取而代之的是当前目录,"any other path"(根据需要))。 where.exe 不仅将? 通配符视为一个字符(与dir 命令不同,它是0 或1),而且忽略8.3 命名。因此,它将.avi 扩展完全按照所写的方式处理(而不是“开头”.avidir 或标准的for 循环会这样做)。

无论如何,请随意尝试一下:

@Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "=" & For /F Delims^= %%G In ('(Set PATHEXT^=^) ^& %__AppDir__%where.exe
 "%~dp0.":"?*_??.??.??_??-??_?*.avi" 2^> NUL') Do (Set "=%%~nG"
    SetLocal EnableDelayedExpansion & For %%H In ("\!:_=\!") Do (
        EndLocal & For %%I In ("%%~pH..\..") Do (Set "=%%~pI"
            SetLocal EnableDelayedExpansion & Set "=!:~1,-1!"
            For %%J In ("!:\=_!") Do (EndLocal & %__AppDir__%robocopy.exe ^
             "%%~dpG." "%%~dpG%%~J" "%%~nxG" /Mov 1> NUL))))

如果您想要进一步的健壮性,并且不希望使用更合适的脚本技术,那么以下看起来非常复杂的版本是相同的代码,除了它使用findstr验证日期和时间顺序。它使用从 1970 年初到 2021 年底的所有日期过滤那些在 avi 文件名中包含以下模式 _yy.MM.dd_hh-mm_ 的 avi 文件:

@Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "=" & For /F Delims^= %%G In ('(Set PATHEXT^=^) ^& %__AppDir__%where.exe
 "%~dp0.":"?*_??.??.??_??-??_?*.avi" 2^> NUL ^| %__AppDir__%findstr.exe
 /RC:"_[789][0123456789].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[789][0123456789].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[789][0123456789].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[789][0123456789].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[789][0123456789].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[789][0123456789].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
 /C:"_[789][0123456789].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[789][0123456789].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[789][0123456789].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[789][0123456789].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[789][0123456789].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[789][0123456789].1[012].3[01]_2[0123]-[012345][0123456789]_"
 /C:"_[01][0123456789].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[01][0123456789].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[01][0123456789].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[01][0123456789].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[01][0123456789].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[01][0123456789].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
 /C:"_[01][0123456789].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[01][0123456789].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[01][0123456789].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[01][0123456789].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
 /C:"_[01][0123456789].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
 /C:"_[01][0123456789].1[012].3[01]_2[0123]-[012345][0123456789]_"
 /C:"_2[01].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_2[01].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
 /C:"_2[01].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_2[01].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
 /C:"_2[01].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
 /C:"_2[01].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
 /C:"_2[01].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_2[01].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
 /C:"_2[01].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
 /C:"_2[01].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
 /C:"_2[01].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
 /C:"_2[01].1[012].3[01]_2[0123]-[012345][0123456789]_"') Do (Set "=%%~nG"
    SetLocal EnableDelayedExpansion & For %%H In ("\!:_=\!") Do (
        EndLocal & For %%I In ("%%~pH..\..") Do (Set "=%%~pI"
            SetLocal EnableDelayedExpansion & Set "=!:~1,-1!"
            For %%J In ("!:\=_!") Do (EndLocal & %__AppDir__%robocopy.exe ^
             "%%~dpG." "%%~dpG%%~J" "%%~nxG" /Mov 1> NUL))))

【讨论】:

感谢您的回答,但与 Magoos 的回答相比,这看起来真的很复杂。如果另一个不起作用,我会试试你的。 @Benjamin,如果您正确查看我的答案,我提供的第一个示例,就像 Magoo 和您自己的一样,不能确定有效的日期序列,它比他们的复杂得多,一个 hack短得多,而且我也会打赌更健壮。我已经提到的第二个例子看起来很复杂,代码和方法完全相同,只是添加了一些 findstr 匹配以进行日期验证。如果不需要日期验证,我的开场响应比较好,不计较,没有calls,没有goto的,没有8.3命名,没有md,更好。选择权在你!

以上是关于批处理:从文件名(子字符串)创建文件夹的主要内容,如果未能解决你的问题,请参考以下文章

使用文件名的子字符串创建文件夹

Hive:从文本文件创建表。处理特殊字符

如何从字符串创建文件并通过 FormData 发送

批处理删除当前文件夹下所有指定类型文件(包括子目录)

Python 从 JSON 文件创建树

创建批处理文件以在多个文件夹中创建多个文件夹