如何在批处理中高效复制MKV/MP4文件的创建、修改等属性?
优化视频文件属性复制的批处理方案
问题背景
在处理.mkv、.mp4视频格式转换的批处理工作中,目前通过三次独立调用PowerShell复制文件的CreationTime、LastWriteTime、LastAccessTime属性,但多次启动PowerShell进程导致处理速度偏低。需要实现单次PowerShell调用完成多属性复制,或找到无需PowerShell的更快方案,同时尽可能复制所有可修改的文件属性。
现有低效代码片段:
powershell ^(ls '!orig-file!'^).CreationTime = ^(ls '%%I'^).CreationTime powershell ^(ls '!orig-file!'^).LastWriteTime = ^(ls '%%I'^).LastWriteTime powershell ^(ls '!orig-file!'^).LastAccessTime = ^(ls '%%I'^).LastAccessTime
注:%%I为循环中的源文件路径,!output!为转换后的目标文件路径
优化方案
1. 单次PowerShell调用完成多属性复制
通过一次性启动PowerShell进程,完成所有时间属性(含UTC时间)、文件系统属性(只读、隐藏等)的复制,大幅减少进程启动开销。替换原有三行PowerShell命令为以下单行命令:
powershell $src=Get-Item '%%I'; $dest=Get-Item '!output!'; $dest.CreationTime=$src.CreationTime; $dest.LastWriteTime=$src.LastWriteTime; $dest.LastAccessTime=$src.LastAccessTime; $dest.CreationTimeUtc=$src.CreationTimeUtc; $dest.LastWriteTimeUtc=$src.LastWriteTimeUtc; $dest.LastAccessTimeUtc=$src.LastAccessTimeUtc; $dest.Attributes=$src.Attributes
- 该命令会复制源文件的所有时间戳属性(本地时间+UTC时间)以及文件系统属性(如只读、隐藏、存档等)
- 仅启动一次PowerShell进程,相比三次调用速度提升显著
2. 无需PowerShell的替代方案(需第三方工具)
若完全不想依赖PowerShell,可使用轻量级命令行工具nircmd,通过以下命令复制时间属性:
nircmd setfiletime "!output!" "%%I" "%%I" "%%I"
- 语法:
nircmd setfiletime <目标文件> <创建时间> <修改时间> <访问时间>,用源文件路径代替时间参数即可直接复制对应属性 - 需提前下载
nircmd.exe并放入系统PATH或批处理同目录,否则需指定完整路径
修改后的完整批处理代码
@ECHO OFF SETlocal SET drive="~dp0" SET string=%CD% SETlocal EnableExtensions EnableDelayedExpansion SET "drive=G:" SET ^"Exclude-AlltheseFolders=#snapshot,output^" SET "Convert_Audio1=Y" SET "Convert_TO=aac" SET "Codec1=eac3" SET "Codec2=flac" SET "Codec3=dts" &SET "Codec4=pcm_s24le" &SET "Codec5=aac" &SET "Codec6=ac3" SET "KeepExtraAudio_NoOrig=Y" SET "KeepOrigAudio=N" SET "OverWriteFiles=N" SET "ExtractTime=Y" SET "time_to_start_from=00:00:10" &SET "duration_to_capture=00:00:15" SET "TempFilePath=C:\Windows\Temp\ffmpeg-CountingScript-AllFilesInCurrentDirlist%DT%--%HH%.%MM%.%SS%.txt" IF !ExtractTime! == Y ( SET "FFmpgextractTime= -ss !time_to_start_from! -t !duration_to_capture!") ECHO THIS IS JMK1 IF !OverWriteFiles! == Y (SET "OverWrite=-y ") ELSE (SET "OverWrite=-n ") IF !Convert_Audio1! == Y ( IF !Convert_TO! == aac ( set "Convert_TO=libfdk_aac" ) SET "FFmpgConvertStream1=-c:a:0 !Convert_TO! -b:a:0 640k -disposition:a:0 default" IF !KeepExtraAudio_NoOrig! == Y ( SET "FFmpgXtraStream=-c:a copy" ) IF !KeepOrigAudio! == Y ( SET "KeepOrigAudio_ffMpg=-map 0:a:0? -c:a:0 copy" SET "KeepExtraAudio_NoOrig=Y" ) ) ELSE ( SET "ConvertCodecs=1" SET "KeepOrigAudio_ffMpg=-c:a:0 copy" SET "KeepExtraAudio_NoOrig=Y" ) SET "delm1=^\^>" /c:"^\^<" SET ^"Exclude-Final=/c:\"\^<!Exclude-AlltheseFolders!\^>"^" SET ^"Exclude-Final=!Exclude-Final:,=%delm1%!^" dir *.mkv *.mp4 /A-D-H /B /S |findstr /R %Exclude-Final% /v /i>%TempFilePath% Echo: These are the folders being Excluded: "!Exclude-AlltheseFolders!" SET "FINALCOMMAND=" SETlocal EnableExtensions DisableDelayedExpansion SET "ProgramFolder=C:\Program Files\FFmpeg-v2020\bin" SET "ProbeOptions=-v quiet -select_streams a:0 -show_entries "stream^^=codec_name" -of json" SET "FilesFound=0" & SET "FilesEncoded=0" & SET "output=" for /F "delims=" %%I in (%TempFilePath%) do ( SET "output=%drive%%%~pI%%~nxI" & SET folder=%drive%%%~pI & SET "filename=%%~nxI" & SET /A FilesFound+=1 SETlocal EnableExtensions EnableDelayedExpansion IF exist "%drive%%%~pIoutput\%%~nxI" (SET "Convert_Audio1=N") ELSE SET "Convert_Audio1=Y" IF "!Convert_Audio1!" == "Y" ( SET "AudioCodec=" & SET "ConvertCodecs=" for /F "eol={ tokens=1,2 delims=,:[ ]{} " %%B in ('""%ProgramFolder%\ffprobe.exe" %ProbeOptions% "%%I""') do ( IF "%%~B" == "codec_name" ( IF not defined AudioCodec ( SET "AudioCodec=%%~C" ) IF "%%~C" == "%Codec1%" (SET "ConvertCodecs=1" ) else IF "%%~C" == "%Codec2%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C ) else IF "%%~C" == "%Codec3%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C ) else IF "%%~C" == "%Codec4%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C ) else IF "%%~C" == "%Codec5%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C ) else IF "%%~C" == "%Codec6%" (SET "ConvertCodecs=1" & ECHO Codec is: %%~C ) ) ) IF !ConvertCodecs! == 1 ( ECHO [91m==!TIME!================[0m!Codec1! [94min[0m- %%I [91m=========[0m ) IF !ConvertCodecs! == 1 ( IF /I "!output!" == "%%I" ( SET "output=%~dp0output\!filename!" MKDIR "%~dp0output\" ) ELSE ( MKDIR "%drive%%%~pI" ) SET "FINALCOMMAND=ffmpeg !OverWrite!-hide_banner%FFmpgextractTime% -loglevel quiet -hwaccel auto -stats -i "%%I" -map 0:v -map_metadata 0 -movflags use_metadata_tags -map 0:a? -map 0:s:0? -c:s:0 copy -c:v copy %FFmpgXtraStream% %FFmpgConvertStream1% %KeepOrigAudio_ffMpg% "!output!" " ECHO this is the command1: !FINALCOMMAND! !FINALCOMMAND! ECHO [91m=====[0mCOMPLETE[91m===============[0m . IF not %FilesEncoded% == 0 ECHO This one was a failure. Count Encoded so far: "%FilesEncoded%" IF not errorlevel 1 SET /A FilesEncoded+=1 powershell $src=Get-Item '%%I'; $dest=Get-Item '!output!'; $dest.CreationTime=$src.CreationTime; $dest.LastWriteTime=$src.LastWriteTime; $dest.LastAccessTime=$src.LastAccessTime; $dest.CreationTimeUtc=$src.CreationTimeUtc; $dest.LastWriteTimeUtc=$src.LastWriteTimeUtc; $dest.LastAccessTimeUtc=$src.LastAccessTimeUtc; $dest.Attributes=$src.Attributes for /F "delims=" %%p in ("!ConvertCodecs!") do ( ECHO THIS IS P: %%p ENDLOCAL SET /A FilesEncoded=%%p+FilesEncoded ) ) ELSE ( ECHO ##NOT PROCESSING##: !TIME! - %%I ## & ECHO [91m=====--[0mFILE ALREADY EXISTS! [91m======================[0m Next File [91m===========================[0m ) ) ) IF %FilesFound% == 1 ( SET "PluralS=" ) else SET "PluralS=s" ECHO [91m***************************************************************************************[0m ECHO Re-encoded %FilesEncoded% of %FilesFound% video file%PluralS%. ECHO [91m***************************************************************************************[0m endlocal endlocal endlocal exit /b
内容的提问来源于stack exchange,提问作者hsgg4
相关产品推荐
相关产品推荐

