【问题标题】:BATCH: How can I copy files to directory based on file nameBATCH:如何根据文件名将文件复制到目录
【发布时间】:2018-05-21 20:52:18
【问题描述】:

我有很多文件要为我工作的公司存档。我对批处理脚本有点熟悉,但我不太了解,无法完全理解我想要的。

我正在尝试将一些基于文件名开头的文件复制到我们 NAS 上的文件夹中。这些文件是 7z 文件,它们的结构是这样的:

  • 5476 宝马把手-A.7z
  • 5487 雪佛兰-Imp.7z
  • 5986 本田 Lid-Upper.7z

等等

文件结构是这样的:开头的四个数字是我们公司的工作编号。在我们的 NAS 上,我们有一个存档目录,其中包含如下文件夹:

  • _5000-5999
  • _6000-6999

在这些文件夹中是文件夹,每个文件夹将包含 250 个存档文件。它们的格式如下:

  • _5000-5249
  • _5250-5499
  • _5500-5749
  • _5750-5999

我正在寻找创建一个批处理文件,我可以将这些 7z 文件拖到该文件上,它会读取文件名的前四个数字,并将其复制到我们 NAS 上的正确文件夹中。

例如文件:

  • 5476 宝马把手-A.7z
  • 5487 雪佛兰-Imp.7z

会复制到

\nas01\存档\_5000-5999\_5250-5499

等等。

我搞砸的主要代码是这样的:

@echo off 
for /f "delims=" %%i in ('dir /b /a-d *.7z') do (
set "filename1=%%~i"
setlocal enabledelayedexpansion
set "folder1=!filename1:~0,4!"
mkdir "!folder1!" 2>nul
copy "!filename1!" "!folder1!" >nul
)

这行对我不起作用:

set "folder1=!filename1:~0,4!"

如何创建某种变量来检查文件名、在必要时创建文件夹并将其复制到正确的文件夹中?我将不胜感激!

-达斯汀

【问题讨论】:

  • 考虑将您的问题分解成更小更易于管理的块。例如,一开始您可能应该将文件名转换为数字,然后使用该数字确定它的去向,然后根据该数字移动文件。所以你的第一个问题可能是如何获取每个文件名并将数字放入批处理变量中。
  • @DustinH,我看不出那个 SET 命令有什么问题。您启用了延迟扩展,并且您正确引用了该变量。所以你必须更详细地解释它是如何不起作用的。
  • @Squashman 对不起,我的意思是这不是我需要的。它只是创建一个与文件号相同的文件夹。所以 5768 BWM.7z 将被移动到名为 5768 的文件夹中。我希望它进入 _5000-5999\ _5750-5999
  • 给你一点提示。 IF !folder1! GEQ 5000 IF !folder1! LEQ 5999 SET "subfolder1=_5000-5999"
  • 感谢你们的帮助!我想通了,会发布答案。

标签: windows batch-file cmd


【解决方案1】:

感谢评论者的帮助,我能够弄清楚。我对如何构建脚本有疑问,但我明白了!

@echo off
for /f "delims=" %%i in ('dir /b /a-d *.7z') do (
set "filename=%%~i"
setlocal enabledelayedexpansion
set "folder1=!filename:~0,4!"
set "subfolder1=_!folder1:~0,1!000-!folder1:~0,1!999"
set "firstdigit=!filename:~0,1!"
set "parent=\\nas01\The_Archives"

REM CONDITIONAL STATEMENTS
IF !folder1! GEQ !firstdigit!000 IF !folder1! LEQ !firstdigit!249 SET "subfolder2=_!firstdigit!000-!firstdigit!249"
IF !folder1! GEQ !firstdigit!250 IF !folder1! LEQ !firstdigit!499 SET "subfolder2=_!firstdigit!250-!firstdigit!499"
IF !folder1! GEQ !firstdigit!500 IF !folder1! LEQ !firstdigit!749 SET "subfolder2=_!firstdigit!500-!firstdigit!749"
IF !folder1! GEQ !firstdigit!750 IF !folder1! LEQ !firstdigit!999 SET "subfolder2=_!firstdigit!750-!firstdigit!999"

mkdir "!parent!\!subfolder1!\!subfolder2!" 2>nul
copy "!filename!" "!parent!\!subfolder1!\!subfolder2!" >nul
)

我今天了解了有关 Batch 的更多信息,所以这一天度过了愉快的一天!

有没有一种方法可以使您只需单击文件并将其拖动到此批处理脚本中,而不是抓取文件夹中的所有 7z 文件?

【讨论】:

  • 恭喜!对于文件名中 1000 到 9999 范围内的数字,效果很好。
  • @Mofi 感谢您的回复!有没有办法让这只是一个点击和拖动脚本?目前它会提取文件夹中的所有 .7z 文件。我知道我需要修改第 2 行 for /f "delims=" %%i in ('dir /b /a-d *.7z') do (
  • for /f "delims=" %%i in ('dir /b /a-d *.7z') 替换为for %%I in (%*) do 以处理所有指定为启动批处理文件的参数的文件,当一个或多个文件被拖放到批处理文件时会发生这种情况。但是,如果至少使用 1 个参数启动,请在您的批处理文件中进行测试,请参阅我更新的答案。在命令copy 之后,您需要添加endlocal 命令,否则您会遇到太多文件的堆栈溢出而无法处理。阅读 this answer 了解有关命令 SETLOCALENDLOCAL 的详细信息。
【解决方案2】:

这是此任务的完整批处理代码,主要使用算术表达式和字符串连接来确定源文件夹中每个 *.7z 文件的目标文件夹路径,或者通过参数将名称传递给批处理文件。

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem Source path is the directory of the batch file ending with a backslash.
set "SourcePath=%~dp0"
rem Target path is a UNC path also ending with a backslash.
set "TargetPath=\\nas01\archive\"

rem Run subroutine ProcessFile for each *.7z file in source folder
rem if not at least 1 file was specified on command line to process.
rem Otherwise process the files of which names are passed as arguments
rem to this batch file on starting it. 
if "%~1" == "" (
    for %%I in ("%SourcePath%*.7z") do call :ProcessFile "%%I"
) else (
    for %%I in (%*) do call :ProcessFile "%%~I"
)

rem Restore previous environment and exit this batch file.
endlocal
goto :EOF


rem The subroutine ProcessFile determines the target folders based on
rem first part of the file name separated with one or more spaces from
rem rest of the file name which should be a positive integer number.

rem File names not starting with a valid number are copied (or moved)
rem to the folder _0000-0249\_0000-0249 in specified target folder.

rem For file numbers less than 1000 an extra code is added to copy (or move)
rem those files into folders also having at least 4 digit numbers in name.

:ProcessFile
for /F %%J in ("%~n1") do set "FileNumber=%%J"
set /A FolderNumber1=(FileNumber / 1000) * 1000
set /A FolderNumber2=FolderNumber1 + 999

set /A FolderNumber3=FileNumber - FolderNumber1
if %FolderNumber3% LSS 250 (
    set "FolderNumber3=%FolderNumber1%"
    set /A FolderNumber4=FolderNumber1 + 249
    goto BuildFolderPath
)
if %FolderNumber3% LSS 500 (
    set /A FolderNumber3=FolderNumber1 + 250
    set /A FolderNumber4=FolderNumber1 + 499
    goto BuildFolderPath
)
if %FolderNumber3% LSS 750 (
    set /A FolderNumber3=FolderNumber1 + 500
    set /A FolderNumber4=FolderNumber1 + 749
    goto BuildFolderPath
)
set /A FolderNumber3=FolderNumber1 + 750
set "FolderNumber4=%FolderNumber2%"

:BuildFolderPath
if %FolderNumber1% == 0 (
    set "FolderNumber1=0000"
    set "FolderNumber2=0%FolderNumber2%"
    set "FolderNumber4=0%FolderNumber4%"
    if %FolderNumber3% == 0 (
        set "FolderNumber3=0000"
    ) else (
        set "FolderNumber3=0%FolderNumber3%"
    )
)
set "TargetFolder=%TargetPath%_%FolderNumber1%-%FolderNumber2%\_%FolderNumber3%-%FolderNumber4%"

mkdir "%TargetFolder%" 2>nul
copy /Y "%~1" "%TargetFolder%\" >nul
rem move /Y "%~1" "%TargetFolder%\" >nul
goto :EOF

使用子例程而不是在 FOR 循环中执行所有操作,因为这使得任务更容易编码,因为可以在子例程中使用 GOTO。并且对于子例程,不需要使用延迟的环境变量扩展,这在 *.7z 文件曾经包含感叹号的情况下很有帮助。

此解决方案的主要优点:它适用于文件名中 0 到 2147482999 范围内的数字。

要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

  • call /?
  • copy /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • mkdir /?
  • move /?
  • rem /?
  • setlocal /?

另请阅读有关 Using Command Redirection Operators 的 Microsoft 文章,其中解释了 >nul2>nul 用于将成功和错误消息重定向到设备 NUL 以抑制它们。

【讨论】:

    猜你喜欢
    • 2019-10-30
    • 2011-11-30
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 2019-11-26
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多