【问题标题】:Move files to a subdirectory将文件移动到子目录
【发布时间】:2020-03-15 13:50:54
【问题描述】:

借助一些很大的帮助 (Create subdirectory under each directory containing a file),我在任何包含 .jpg 照片的目录中添加了一个 \pre 子目录。

我想将任何 .jpg 文件从其当前目录移动到 \pre 子目录中。我试过的脚本是:

FOR /R c:\temp %G IN (*.JPG) DO pushd %~dpG && if exist *.jpg move *.jpg pre\ && popd

脚本移动了 .jpg 文件。问题是脚本移动文件然后跟随到 \pre 目录并尝试再次移动。

已使用第一段中链接的脚本创建了预目录。

例如,目录 A\B\C 被处理为 A\B\C\pre。此脚本扫描 A/B/C 并将 .jpgs 移动到 \A\B\C\pre。然后它沿着目录树进入 A\B\C\pre 并尝试再次移动 .jpg 文件

【问题讨论】:

    标签: windows for-loop cmd


    【解决方案1】:

    下面的脚本呢:

    @echo off
    rem // Enumerate the directory tree:
    for /D /R "C:\TEMP" %%G in ("*") do (
        rem // Check whether current directory is not named `pre`:
        if /I not "%%~nxG" == "pre" (
            rem // Check whether there are files:
            if exist "%%~G\*.jpg" (
                rem // Create sub-directory called `pre`:
                md "%%~G\pre" 2> nul
                rem // Move files into the sub-directory:
                move "%%~G\*.jpg" "%%~G\pre"
            )
        )
    )
    

    或者直接在命令提示符下:

    @for /D /R "C:\TEMP" %G in ("*") do @if /I not "%~nxG" == "pre" if exist "%~G\*.jpg" md "%~G\pre" 2> nul & move "%~G\*.jpg" "%~G\pre"
    

    【讨论】:

    • 非常感谢!我将这个脚本用于我的任务并且效果很好。这些评论很有帮助,因此我可以理解它在做什么
    【解决方案2】:

    我建议将此批处理文件用于文件移动任务。

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    for /F "delims=" %%I in ('dir "C:\Temp\*.jpg" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /C:"\\pre\\" /C:"\\post\\"') do (
        if not exist "%%~dpIpre\" md "%%~dpIpre"
        move /Y "%%I" "%%~dpIpre\"
    )
    endlocal
    

    FOR 在后台启动另一个带有%ComSpec% /c 的命令进程,并将指定的命令行作为附加参数附加。因此,在后台将 Windows 安装到 C:\Windows 中执行:

    C:\Windows\System32\cmd.exe /c dir "C:\Temp\*.jpg" /A-D /B /S 2>nul | %SystemRoot%\System32\findstr.exe /I /L /V /C:"\\pre\\" /C:"\\post\\"
    

    DIR由后台命令进程执行搜索

    • 在目录C:\Temp 及其所有子目录中,因为选项/S
    • 仅用于文件,因为选项 /A-D(属性不是目录)
    • 匹配通配符模式*.jpg
    • 并由于选项/B而以裸格式输出
    • 由于选项/S,仅包含具有完整路径的文件名。

    这个文件名列表从后台命令进程的STDOUT(标准输出)重定向操作符|STDIN(标准输入) FINDSTR 搜索的内容

    • 由于选项/I而包含不区分大小写的行
    • 文字字符串\pre\ 或文字字符串\post\
    • 并由于选项/V 输出反转结果,即行包含\pre\\post\

    所以 FINDSTR 在这里用作过滤器,以从 DIR 输出的 *.jpg 文件名列表中获取完整路径,只是那些没有@的文件名987654340@ 或 \post\ 在其路径中排除已位于名称为 prepost 的两个子目录之一中的 JPEG 文件。

    2>nul 在命令 DIR 的参数之后,如果在 C:\Temp 及其子目录中找不到任何 *.jpg 文件名,则会抑制 DIR 输出的错误消息通过将写入STDERR(标准错误)的错误消息重定向到设备NUL

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

    FOR 与选项 /F 捕获所有写入以处理由 FINDSTR 启动的命令进程的 STDOUT 并在之后逐行处理此输出开始cmd.exe 自行终止。在这里处理捕获的文件名列表非常重要,并且不要在文件系统返回的另一个文件名之后迭代一个文件名,因为通配符模式匹配的文件 *.jpg 在目录结构中的每次循环迭代期间都会移动.因此,与 *.jpg 匹配的目录条目在每次循环迭代时都会发生变化,因此需要在移动文件之前将文件名列表加载到内存中。

    FOR 带有选项 /F 会忽略此处没有出现的空行。

    FOR 带有选项/F 将使用普通空格和水平制表符作为字符串分隔符将每一行拆分为子字符串,并将仅将第一个空格/制表符分隔的字符串分配给指定的循环变量I if不以默认行尾字符 ; 开头,在这种情况下,该行将像空行一样被完全忽略。

    具有完整路径的文件名不能以; 开头。所以这里不能修改默认的eol=;。但是行拆分行为会适得其反,因为完整的限定文件名可以包含一个或多个空格。出于这个原因,选项delims= 用于定义字符串分隔符的空列表,它完全禁用行拆分行为。

    因此,DIR 输出的每个完整文件名在由 FINDSTR 过滤掉的路径中不包含 \pre\\post\ 被分配给循环变量 I 一个一个接一个。

    接下来检查当前 JPEG 文件是否存在子文件夹 pre,如果不存在则创建此文件夹。然后将当前 JPEG 文件移动到子目录 pre 中,并以完全相同的文件名覆盖 pre 中的文件。

    所以这个批处理文件可以在C:\Temp 上执行多次,因为它会忽略所有子目录prepost 中的所有*.jpg 文件

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

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

    【讨论】:

    • 感谢您提供广泛的答案以及非常有用的参考资料和解释性说明。虽然这次我没有使用你的脚本来执行任务,但这将是未来的绝佳参考
    【解决方案3】:

    在新的“pre”目录中进行操作的问题是通过在枚举目录之前获取目录列表来解决的。

    使用单行命令执行此操作可能会很好,但这比单行命令中容易执行的操作要复杂一些。

    这是一个可以执行此操作的 PowerShell 脚本。如果您在受支持的 Windows 平台上,则可以使用 PowerShell。此脚本需要 PowerShell 5.1 或更高版本。如果您无法访问当前的 PowerShell,可以更改代码以使其正常工作。如果您对移动正确的文件感到满意,请从 mkdirMove-Item 命令中删除 -WhatIf

    === Move-JpegToPre.ps1

    $dirs = Get-ChildItem -Directory -Path "C:/src/t"
    $extent = 'jpg'
    
    foreach ($dir in $dirs) {
        if (Test-Path -Path "$($dir.FullName)/*.$($extent)") {
            if (-not (Test-Path -Path "$($dir.FullName)/pre")) {
                mkdir "$($dir.FullName)/pre"
            }
            Move-Item -Path "$($dir.FullName)/*.$($extent)" -Destination "$($dir.FullName)/pre" -WhatIf
        }
    }
    

    在 cmd.exe shell 中,可以通过以下方式调用它:

    powershell -NoLogo -NoProfile -File Move-JpegToPre.ps1
    

    【讨论】:

    • 非常感谢。很抱歉,我没有接受这个答案作为解决方案。您的回答根本没有“错误”。我碰巧使用了另一个建议的解决方案,但会记住您的解决方案,以备将来需要执行类似操作时使用。
    猜你喜欢
    • 1970-01-01
    • 2017-05-27
    • 1970-01-01
    • 2014-06-06
    • 2013-04-14
    • 2023-04-07
    • 2021-02-28
    • 1970-01-01
    • 2016-06-09
    相关资源
    最近更新 更多