【问题标题】:Create a directory based on the start of a file name根据文件名的开头创建目录
【发布时间】:2021-11-07 04:26:25
【问题描述】:

我希望迁移大量文件(电子书),基于开头的文本(作者姓名) 我尝试编写批处理文件,但缺少一些基本内容。

几乎所有的文件都是

"Authors name - Book title.extention"
"Authors name - [Book series] - Book title.extention"

我希望通过一个脚本(最好是批处理文件)传递每个文件,该脚本标识作者的姓名部分(基本上是寻找第一个“ - ”,然后根据开始修剪的字符串创建一个目录。

我根据研究制作了一个 FOR & IF 命令,但其中一部分不起作用

SET str=%1%

FOR /L %%a in (1,1,50) do (

    if "!str:~%%a,3!"==" - " (
        set DName=!str:~0,%%a-1!
        MD DName
        MOVE %1 DName
        EXIT
    )
)

我不完全理解字符串截断命令“STRING:~STARTING POSITIONWITHINSTRING,STRINGLENGTH”。我一直在寻找人们使用它的例子,就像它只是工作一样,但我找不到解释它是如何操作的文档。

我的要求可以作为 BATCH 文件完成吗?我做错了什么?

【问题讨论】:

  • 它是%variable:~start_index,substring_length%。索引从 0 开始。
  • 当您说“我无法找到解释其操作方式的文档。”时,我确信当您打开命令提示符窗口时,输入 set /? 并按 [ENTER] key,里面有环境变量扩展和替换的信息。

标签: batch-file


【解决方案1】:

使用小技巧去除分隔符字符串和标题(注意:它不适用于推荐的语法set "var=value",因此请特别注意不要在set folder... 命令中添加任何空格)

@echo off
setlocal
set "file=%~1"
SET "file=Authors name - Book title.extention"
set folder=%file: - =&REM %
ECHO md "%folder%" 2>nul
ECHO move "%file%" "%folder%\"

删除SET 行(仅用于演示)和(在验证其按预期工作后),这两个ECHO 命令。

即使作者姓名包含-,也可以使用,例如Karl-Heinz Writer,因为它会被字符串<space><dash><space> 分割,而for 无法做到这一点。

【讨论】:

    【解决方案2】:

    该任务可以使用带有以下代码的批处理文件来完成:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    for /F "eol=| delims=" %%I in ('dir "* - *" /A-D /B 2^>nul') do for /F "eol=| delims=-" %%J in ("%%I") do (
        if not exist "%%~nxJ" md "%%~nxJ"
        if exist "%%~nxJ" move "%%I" "%%~nxJ\"
    )
    endlocal
    

    命令DIR搜索

    • 在当前目录中
    • 仅用于文件,因为选项 /A-D(属性不是目录)
    • 其中的文件名与通配符模式* - *匹配
    • 并以裸格式输出找到的文件名,因为选项 /B 意味着只有文件名和扩展名而没有路径。

    可能找不到符合条件的文件名,这将导致写入错误消息以处理 STDERR。通过将其重定向到设备 NUL 来抑制此可能的错误消息。

    阅读有关Using command redirection operators 的Microsoft 文档,了解2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用插入字符 ^ 转义,以便在 Windows 命令解释器在执行命令 FOR 之前处理此命令行时解释为文字字符> 在后台启动的单独命令进程中执行嵌入的dir 命令行,cmd.exe /c' 中的命令行作为附加参数附加。

    DIR在后台命令过程中的输出被FOR捕获并逐行处理。

    FOR 使用选项/F 会跳过此处根本不会出现的空行。 FOR 处理捕获的行的默认行为是使用普通空格和水平制表符作为字符串分隔符将行拆分为子字符串,并将第一个空格/制表符分隔的字符串分配给指定的循环变量@987654335 @。 DIR 输出的文件名不需要这种行拆分行为,因为文件名肯定包含空格,并且需要整个文件名来进一步处理文件。出于这个原因,选项delims= 用于定义一个空的字符串分隔符列表以关闭行拆分行为,从而获取分配给循环变量I 的扩展名的文件名。

    FOR on using option /F 也将查看第一个子字符串的第一个字符,如果它以分号开头,它是默认的行尾字符,则会忽略该行以进行进一步处理。作者姓名不太可能以; 开头,但仍然可以使用选项eol=| 重新定义行尾字符,并带有竖线,任何文件名都不能包含该竖线。

    第二个for /F 用于分割文件名字符串,使用- 作为字符串分隔符,并仅获取分配给指定循环变量J 的第一个子字符串。只要作者姓名不包含-,这适用于您的文件名。所以J在每个文件名上定义为Authors name ,连字符后面的空格作为尾随空格。

    有关 Naming Files, Paths, and Namespaces 的 Microsoft 文档解释了 Windows 如何通过删除尾随空格/点来处理文件/文件夹名称。出于这个原因,%%~nxJ 用于引用直到第一个 - 的字符串,根据作者的姓名是否已经存在,独立于子目录中删除了尾随空格和点。

    第一个IF条件用于判断作者名字的子目录是否不存在,如果不存在子目录,则使用MD命令创建目录.

    第二个IF条件用于验证根据作者姓名的子目录现在是否存在,如果符合预期,则使用命令MOVE移动文件进入子目录。

    上面的一个变体是:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    for /F "eol=| delims=" %%I in ('dir "* - *" /A-D /B 2^>nul') do for /F "eol=| tokens=1* delims=-" %%J in ("%%I") do (
        if not exist "%%~nxJ" md "%%~nxJ"
        if exist "%%~nxJ" for /F "eol=| tokens=*" %%L in ("%%K") do move "%%I" "%%~nxJ\%%L"
    )
    endlocal
    

    不同之处在于,每个文件都被移动到具有新名称的子目录中,直到第一个连字符和第一个连字符之后的所有空格都从文件名中删除,以使子目录中的文件在文件名中没有作者姓名。

    在不使用环境变量和延迟变量扩展的情况下使用命令行的主要优点是此代码也适用于包含一个或多个感叹号的文件名。

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

    • dir /?
    • echo /?
    • endlocal /?
    • for /?
    • if /?
    • md /?
    • move /?
    • setlocal /?

    【讨论】:

    • 谢谢。太棒了。感谢您解释它是如何工作的。
    【解决方案3】:

    这是另一种使用多个字符作为分隔符来拆分字符串的方法。这使用了在执行代码行之前如何扩展变量值的一些非常独特的方面。

    SET "var=Authors name - Book title.extention"
    set "Author=%var: - =" & set "Book=%"
    
    Echo Author=%Author%
    echo Book=%Book%
    

    这会给输出。

    Author=Authors name
    Book=Book title.extention
    

    【讨论】: