ForFiles批量移动&使用时间戳重命名文件

时间:2017-10-22 05:10:47

标签: windows batch-file cmd

我有一堆* .mp4& * .jpg每小时/每天创建的文件,使用下面的文件结构:

-- before:
c:\video\2017-10-15\21hour\jpg\12.13.15[M][0@0][0].jpg
c:\video\2017-10-15\21hour\mp4\12.13.01-12.14.32[M][0@0][0].mp4
c:\video\2017-10-18\16hour\jpg\21.42.31[M][0@0][0].jpg
c:\video\2017-10-18\16hour\mp4\21.42.31-21.45.38[M][0@0][0].mp4

我想要所有* .jpg& * .mp4文件被移动到c:\ video \&也完全放弃旧名称的日期' date_time' +扩展名,类似于以下内容:

-- after:
c:\video\2017-10-15_12.13.15.jpg
c:\video\2017-10-15_12.13.01-12.14.32.mp4
c:\video\2017-10-18_21.42.31.jpg
c:\video\2017-10-18_21.42.31-21.45.38.mp4

我发现了ForFiles,并且能够使用/ s递归搜索所有子文件夹&移动* .mp4& * .jpg到一个位置:

forfiles /p c:\video\ /s /m *.mp4 /c "cmd /c move @PATH c:\video\"
forfiles /p c:\video\ /s /m *.jpg /c "cmd /c move @PATH c:\video\"

现在进行重命名,此时我几乎可以尝到胜利,因为这给了我想要的文件名输出:

forfiles /p c:\video\ /s /m *.mp4 /c "cmd /c echo @FDATE_@FTIME.mp4"
forfiles /p c:\video\ /s /m *.jpg /c "cmd /c echo @FDATE_@FTIME.jpg"

但是当我用ren / rename / move替换echo时,我得到错误: 该系统找不到指定的路径。 该命令的语法不正确。

但错误只发生在我使用@FDATE和/或@FTIME时。这些变量都可以正常工作:@ FNAME,@ ISDIR,@ FSIZE

forfiles /p c:\video\ /m *.mp4 /c "cmd /c ren @FILE @ISDIR.@EXT"
forfiles /p c:\video\ /m *.mp4 /c "cmd /c rename @FILE @FSIZE.@EXT"
forfiles /p c:\video\ /m *.mp4 /c "cmd /c move @FILE @EXT.@EXT"

一旦我让重命名部分工作,我将结合移动和放大重命名为一个命令...但我无法弄清楚为什么@FDATE& @FTIME不会接受,我的语法有问题,或者这只是ForFiles不允许的内容?

1 个答案:

答案 0 :(得分:1)

问题已解决

首先,仅用echomove替换ren将不起作用,因为它只会检测传递给它的一个参数。这可以解释语法错误。

Microsoft Docsren的语法

ren [<Drive>:][<Path>]<FileName1> <FileName2>

rename [<Drive>:][<Path>]<FileName1> <FileName2>

Microsoft Docsmove的语法

move [{/y | /-y}] [<Source>] [<Target>]

第二,变量@FDATE@FTIME是与语言环境相关的。它们的格式取决于您设备的区域格式,例如@FDATE的格式可以包含/,而@FTIME的格式可以包含:。这些是保留字符,如果使用不当,可能会导致错误。这可以解释路径错误。


使用forfiles

的解决方案

如果要使用forfiles执行此操作,则必须使用for命令,例如this。您可以通过提取诸如this answer中的子字符串来更改日期格式。您可以通过用点:代替冒号.来更改时间格式,如here所述。

您可以一次将/m用于.jpg,然后再次用于.mp4,但是您也可以像this for loop一样使用它。这意味着您必须将示例中的for嵌套在另一个for循环中。

根据move /?,如果仅移动一个文件,则也可以对其重命名。

示例:

setlocal EnableDelayedExpansion

for %%F in (.jpg, .mp4) do (
    for /f "tokens=1,2,3,4 delims=|" %%A in (
        'forfiles /p "C:\video" /s /m "*%%F" /c "cmd /c echo @PATH^|@EXT^|@FDATE^|@FTIME"'
        ) do (
        set filepath=%%~A
        set extension=%%~B
        set datestamp=%%~C
        set timestamp=%%~D
        set parseddate=!datestamp:~6,4!-!datestamp:~3,2!-!datestamp:~0,2!
        move "!filepath!" "C:\video\!parseddate!_!timestamp::=.!.!extension!"
    )
)

endlocal

如果文件数量很大,批处理将需要一些时间。 forfiles必须先读取文件,而for循环也必须遍历该文件列表。另外,forfiles是与语言环境相关的。如果您设备的区域日期格式为2020/02/10,则在我的示例中,子字符串的索引不正确。


使用PowerShell的解决方案

.NET Framework的LastWriteTime属性与语言环境无关,因为它不依赖于设备的区域格式。如果您更喜欢使用UTC,则还有一个LastWriteTimeUtc属性。不过,您仍然必须在批处理文件中使用for循环。

PowerShell的Get-ChildItem返回由-Path参数指定的父文件夹的所有子文件夹,并且可以通过-Recurse参数来递归地这样做。如果您希望结果仅包含.jpg和.mp4文件,则应使用-Include *.jpg, *.mp4。通常,在使用-Include时,您需要将\*附加到路径中,但是由于使用了-Recurse,所以没有必要。

使用Get-ItemProperty-Name参数可以列出FullnameLastWriteTimeExtension属性。为了使for循环更具可读性,必须像$var.Property那样调用它们并将其串联。 ToString()必须用于将LastWriteTime转换为字符串并更改其格式。

示例:

for /f "tokens=1,2 usebackq delims=|" %%A in (
    `powershell -Command "& {Get-ChildItem -Path 'C:\video\*' -Recurse -Include *.jpg, *.mp4 | ForEach {Get-ItemProperty -Path $_.FullName -Name FullName, LastWriteTime, Extension} | ForEach {$_.FullName+'|'+$_.LastWriteTime.ToString('yyyy-MM-dd_HH.mm.ss')+$_.Extension}}"`
) do (
    move "%%~A" "C:\video\%%~B"
)

注意:您可以省略-Path,但是第一个参数必须是路径。