简体   繁体   中英

ForFiles to batch move & rename file(s) with timestamp

I have a bunch of *.mp4 & *.jpg files created on an hourly/daily basis, which use the file structure below:

-- 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

I want all the *.jpg & *.mp4 files to get moved to c:\\video\\ & also completely ditch the old name for 'date_time' + extension, similar to below:

-- 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

I discovered ForFiles, and was able to use the /s to recursively search all subfolders & move the *.mp4 & *.jpg to a single location:

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\"

Now for the renaming, at this point I can almost taste victory as this gives me the desired filename output:

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"

But when I replace echo with either ren/rename/move, I get errors: The system cannot find the path specified. The syntax of the command is incorrect.

But the error only happens when I'm using @FDATE and/or @FTIME. These variables all work fine: @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"

Once I get the renaming part to work, I will combine both the move & rename into one command... But I can't figure out why @FDATE & @FTIME won't take, is there something wrong with my syntax, or is this just something ForFiles doesn't allow?

The problem explained

Firstly, replacing echo with just move or ren alone will not work because it will only detect one argument being passed to it. This could explain the syntax error.

Syntax of ren from Microsoft Docs

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

Syntax of move from Microsoft Docs

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

Secondly, the variables @FDATE and @FTIME are locale-dependent. Their format will depend on your device's regional formatting, eg @FDATE 's format could contain / and @FTIME 's format contains : . These are reserved characters and could result in an error if misused. This could explain the path error.


A solution using forfiles

If you want to do this with forfiles you will have to use the for command, like this . You can change the date format by extracting substrings like in this answer . You can change the time format by replacing the colon : with a dot . , explained here .

You could use /m once for .jpg and again for .mp4, but you could also try it like this for loop . This means you would have to nest the for in the example in another for loop.

According to the move /? , if you move just one file you can rename it as well.

Example:

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

If the file count is large, it will take a little while for batch to finish. forfiles has to read the files first and the for loop has to also go through that file list. Also, forfiles is locale-dependent. If the regional date format of your device is 2020/02/10 then the indices of the substring in my example are not correct.


A solution using PowerShell

.NET Framework's LastWriteTime property is locale-independent, as in it does not dependent on your device's regional formatting. There is also a LastWriteTimeUtc property if you prefer to use UTC. You will still have to use a for loop inside a batch file though.

PowerShell's Get-ChildItem returns all children of the parent folder specified by the -Path parameter and with the -Recurse parameter you can do so recursively. If you want the result to include just .jpg and .mp4 files you should use -Include *.jpg, *.mp4 . Normally when using -Include you would need to append \\* to the path, but since -Recurse is used it's not necessary.

With Get-ItemProperty and the -Name parameter you can list the Fullname , LastWriteTime and Extension properties. To make it more readable for the for loop they have to be called like $var.Property and concatenated. ToString() must be used to convert LastWriteTime to string and change its format.

Example:

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"
)

Note: You can leave out -Path , but then the first argument must be the path.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM