Win10-Batch:字符串替换行为非常奇怪(90% 的情况下有效)

Win10-Batch:字符串替换行为非常奇怪(90% 的情况下有效)

我现在删除了所有内容。为了证明其余代码实际上不是问题的原因,我创建了以下内容dummy.bat

@ECHO OFF & cls 
SETLOCAL EnableDelayedExpansion

ECHO THIS IS EXAMPLE 1 (Not working as expected - look at the end)
ECHO.

set "OutLoc=K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Magic & Mayhem (320)\2010 - Magic & Mayhem (320)\12 - My Kantele.mp3"
set "FileName=12 - My Kantele.mp3"
set "ShortFileName=12 - My Kantele"

ECHO FileName: !FileName!
ECHO ShortFileName: !ShortFileName!

set OutDir=!OutLoc!
call set OutDir=%%OutDir:!FileName!=%%
call set OutLoc=%%OutLoc:!FileName!=%%!!ShortFileName!.mp3

ECHO OutDir: !OutDir!
ECHO OutLoc: !OutLoc!

ECHO.
ECHO.
ECHO THIS IS EXAMPLE 2 (Working as expected)
ECHO.

set "OutLoc=K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Forging The Land Of Thousand Lakes (320)\2010 - Forging The Land Of Thousand Lakes (320)\CD1\08 - Silent Waters.mp3"
set "FileName=08 - Silent Waters.mp3"
set "ShortFileName=08 - Silent Waters"

ECHO FileName: !FileName!
ECHO ShortFileName: !ShortFileName!

set OutDir=!OutLoc!
call set OutDir=%%OutDir:!FileName!=%%
call set OutLoc=%%OutLoc:!FileName!=%%!!ShortFileName!.mp3

ECHO OutDir: !OutDir!
ECHO OutLoc: !OutLoc!

现在让我们看看,纯字符串是否可以复制我想要解决的问题。让我们看看示例 1 和示例 2 的输出,看看有什么不同:

  • 这是示例 1 // 未按预期工作 - 请查看结尾
FileName: 12 - My Kantele.mp3
ShortFileName: 12 - My Kantele
OutDir: K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Magic & Mayhem (320)\2010 - Magic & Mayhem (320)\12 - My Kantele.mp3
OutLoc: K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Magic & Mayhem (320)\2010 - Magic & Mayhem (320)\12 - My Kantele.mp3
  • 这是示例 2 // 按预期工作
FileName: 08 - Silent Waters.mp3
ShortFileName: 08 - Silent Waters
OutDir: K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Forging The Land Of Thousand Lakes (320)\2010 - Forging The Land Of Thousand Lakes (320)\CD1\
OutLoc: K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Forging The Land Of Thousand Lakes (320)\2010 - Forging The Land Of Thousand Lakes (320)\CD1\08 - Silent Waters.mp3

所以很明显,其余代码 - 正如我所说的 - 是不相关的。

这两个示例都使用了我之前测试的原始路径。那么怎么会有所不同呢?两者都只是带有空格和一些括号的字符串……有什么想法吗?字符串替换有长度限制吗?

答案1

在之前的版本中我看到当前发布的这部分代码来自循环for,所以......

我认为你可以通过做一些改变来获得更好的结果set "variables=substring"...


for %%F in ("!FileLoc!")do (

    set FileName=%%~nxF
    set ShortFileName=%%~nF

    ECHO FileName NXF: %%~nxF
    ECHO ShortFileName nF %%~nF

)

    set OutDir=!OutLoc!
    call set OutDir=%%OutDir:!FileName!=%%
    call set OutLoc=%%OutLoc:!FileName!=%%!!ShortFileName!.mp3

    ECHO OutDir: !OutDir!
    ECHO OutLoc: !OutLoc!

  • 删除此循环并使用来自第一个/上一个循环的变量扩展for来定义子字符串,我没有看到需要使用子字符串和另一个变量来定义变量...
set "FileName=%%~nxa"
set "ShortFileName=%%~na"
         
echo\FileName NXA: "%%~nxa"
echo\ShortFileName nA: "%%~na"
          
set "OutDir=!OutLoc!"
set "OutDir=!OutDir:%%~nxa=!"
set "OutLoc=!OutLoc:%%~xa=!.mp3"
   
 
echo\OutDir: "!_OutDir!"
echo\OutLoc: "!_OutLoc!"
  • 定义/使用变量时请记住“双引号”,这样可以避免文件夹/文件名中出现任何特殊字符的问题......

观察:使用if整数 [ EQU, NEQ, LSS, LEQ, GTR, GEQ] 整数(..

在此处输入图片描述


我会添加一些建议,但请理解,我不知道你的内容和/或布局%@RootPath%Aformats.cfg文件。


@echo off && cls

setlocal EnableDelayedExpansion 

rem :: define 2 variable integers in one command/line ::
set /a "_LogEnabled=1, _ResetLog=1"

rem   :: you can remove () from else (command) ::
if !_LogEnabled! equ 1 (echo\Logging is enabled) ELSE echo\Logging is disabled...

rem   :: concatenated rem./ and command in same line ::
rem./ :: defining output path  &&  set "_OutPath=%~dp0output\"
rem./ :: defining input path   &&  set "_InPath=%~dp0tinput\" 
rem./ :: defining batch path   &&  set "_RootPath=%~dp0"      
rem./ :: defining batch drive  &&  set "_InDrive=%~d0\"       

cd /D "!_InPath!"
rem   :: 2>nul ommit any possible warnning/error :: 
if !_ResetLog! equ 1 2>nul del/F /Q "!_RootPath!log.txt"

rem   :: the label :_timestamp will use one fixed date/time output layout ::
rem   :: to define the same timestamp leading by 0 and using the safe way :: 
rem   :: to get current day month and year in batch (see linked question) :: 
call :_timestamp 

if !_LogEnabled! equ 1 echo :: !_dtstamp! :: Starting conversion >>"!_RootPath!log.txt"
    
rem   :: read all listed audio files ::

for /F tokens^=*^usebackq %%Z in ("!_RootPath!aformats.cfg")do for /r %%a in ("*.%%~Z")do (
         
         set "_FileLoc=%%~dpa%%~nxa"
         set "_FileLocRel=%%~dpa%%~nxa"
         set "_FileLocRel=!_FileLocRel:%~dp0tinput\=!"
         
         set "_OutLocRel=%%~dpa%%~nxa"
         set "_OutLocRel=!_OutLocRel:%~dp0tinput\=!"
         set "_OutLoc=!_RootPath!output\!_OutLocRel!"
             
         echo\OUTLOCRELATIVE: "!_OutLocRel!"
         echo\OUTLOC: "!_OutLoc!"
            
         set "_FailedPath=!_RootPath!failed\!_OutLocRel!"
         
         echo\This is the fileloc: "!_FileLoc!"
            
         set "_FileName=%%~nxa"
         set "_ShortFileName=%%~na"
        
         echo\FileName NXA: "%%~nxa"
         echo\ShortFileName nA: "%%~na"
         set "_OutDir=!_OutLoc!"
        
         set "_OutDir=!_OutDir:%%~nxa=!"
         set "_OutLoc=!_OutLoc:%%~xa=!.mp3"
         set "_FailedPath=!_FailedPath:%%~na=!"
            
         echo\OutDir: "!_OutDir!"
         echo\OutLoc: "!_OutLoc!"
         
         echo\Now processing: "!_FileLocRel!"
         
         rem   :: updated date/time ::
         call :_timestamp

         if !_LogEnabled! equ 1 echo :: !_dtstamp! :: Now processing: !_FileLocRel!... >>"!_RootPath!log.txt"   
        
         rem   :: Reading length value into a temp file for later access ::
         !_RootPath!bin\ffprobe.exe" -i "!_FileLoc!" -show_entries format=duration -v quiet -of csv="p=0" >>"!_RootPath!length.temp"
             
         rem   :: These are required to calculate the output bitrate   ::
         "!_RootPath!bin\ffprobe.exe" -i "!_FileLoc!" -show_entries format=size -v quiet -of csv="p=0" >>"!_RootPath!size.temp"
             
         rem   :: Resetting length (in case of corrupted file) ::
         rem   :: also, define 2 integers in one command/line ::
         set /a "_length=0, _size=0" 
         
         rem   :: Reading length from file as a value
         for /F "tokens=*" %%T in ('type "!_RootPath!length.temp"')do set "_length=%%T"
         
         rem   :: Reading size from file as a value
         for /F "tokens=*" %%S in ('type "!_RootPath!size.temp"')do set "_size=%%S"
             
         rem   :: you can concatenate your variables to check the size using just an if ::
         if !_size!!_length! leq 0 (
             echo\!_FileLocRel! seems to be corrupted or invalid. Ignoring file...
             set "_SeemsInvalid=1") ELSE set "_SeemsInvalid=0" 
             
         if !_LogEnabled! equ 1 (
                 rem   :: updated date/time :: 
                 call :_timestamp 
                 echo\:: !_dtstamp! :: Error: !_FileLocRel! seems to be corrupted or invalid. Ignoring file... >>"!_RootPath!log.txt"
                )
        
         rem   :: create your folder and if exist, just ommit any possible warnning/error ::
         2>nul mkdir "!_FailedPath!"
         
         rem   :: Save to ...\failed\ and mute response in CMD :: 
         >nul xcopy /s /z /y "!_FileLoc!" "!_FailedPath!" 
        
         rem   :: Check if output subdirectories exist, if not, create them ::
         if EXIST "!_OutDir!\." (
             echo\DIRECTORY "!_OutDir!" EXISTS - it seems to be at least
             echo\AND This is the outloc: "!_outloc!"
            ) ELSE  mkdir "!_OutDir!"
            
         if !_LogEnabled! equ 1 (
                  rem   :: updated date/time :: 
                 call :_timestamp
                 echo\:: !_dtstamp! :: Making new directory: MKDIR "!_OutDir!" >>"!_RootPath!log.txt"
                )
         rem   ::  replacing [if exist (echo\nothing>>nul..) else do...] to single action :: 
         if not EXIST "!_OutLoc!" if !_SeemsInvalid! equ 0 (
             rem :: define 2 integers in one command/line ::
             set /A "_InBitrate=!_size!*1, _InBitrate=!_InBitrate!/!_length!"
            )
            
         if !_InBitrate! gtr 320000 (
             echo\Target bitrate exceeds 320kbit/s
             set "_InBitrate=320000"
            )
             
         echo\Target bitrate: !_InBitrate! 
         
         if !_LogEnabled! equ 1 (
             rem   :: updated date/time :: 
             call :_timestamp 
             echo\:: !_dtstamp! :: 
             "!_RootPath!bin\ffmpeg.exe" -n -i "!_FileLoc!" -codec:a libmp3lame -b:a !_InBitrate! "!_OutLoc!" >>"!_RootPath!log.txt"
            )
               
         "!_RootPath!bin\ffmpeg.exe" -n -i "!_FileLoc!" -codec:a libmp3lame -b:a !_InBitrate! "!_OutLoc!"
         
         rem   :: Cleaning up temp files for next loop (deleting 2 files in one command) :: 
         2>nul del/F /Q "!_RootPath!length.temp" "!_RootPath!size.temp"
        
        )
    )

if !_LogEnabled! equ 1 (
rem   :: updated date/time :: 
     call :_timestamp 
     echo :: !_dtstamp! :: Conversion finished >>"!_RootPath!log.txt"
    )

endlocal & goto :EOF

:_timestamp < :: Tue 25:08:2020 - 23:36:48.52 :: >
rem   :: Define a pseudo array for the day of the week, which will be ::
rem   :: used in the third for loop to literal define day of the week ::
set "_wday=0-Sun,1-Mon,2-Tue,3-Wed,4-Thu,5-Fri,6-Sat"

rem   :: get day, day of the week, month, year and time (hour), and  ::
rem   :: regardless of location or regardless of any user settings   ::
for /f %%i in ('^^^< nul %__APPDIR__%wbem\wmic.exe os get LocalDateTime^|find "."')do (
     set "_dt=%%i"
     set "_dt=!_dt:~6,2!:!_dt:~4,2!:!_dt:~0,4! - !_dt:~8,2!:!time:~3!"
    )
    
rem   :: Take the day of the week and use a second for loop to check which of elements of %%D in ::
rem   :: pseudo array corresponds, when it occur (%%D equ %%i), will set "_day_of_the_week=%%~E" ::
rem   :: if %%D equ 2 set "day_of_the_week=B", if 2 equ 2 set "_wd=%%~E", and timestamp will set ::
for /f %%i in ('^^^< nul %__APPDIR__%wbem\wmic.exe Path Win32_LocalTime Get DayOfWeek')do (
     for %%L in (!_wday!)do for /f tokens^=1-2^delims^=- %%D in ('echo\%%~L')do if %%D equ %%i (
         set "_wd=%%~E" 
         set "_dtstamp=!_wd!!_dt!"
         exit /b
        )
        )

在此处输入图片描述


答案2

那么怎么会有所不同呢?两者都只是带有空格和一些括号的字符串...有什么想法吗?

示例脚本中的问题似乎是路径&中的文字符号OutLoc&特殊字符在 Windows 的命令行中,似乎导致了意外行为。使用插入&符号转义这两个符号^(因此它们被解释为常规字符)会产生所需的输出:

例如&^&( short-dummy.bat)替换

@ECHO OFF & cls
SETLOCAL EnableDelayedExpansion

@REM [...]

set "OutLoc=K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Magic ^& Mayhem (320)\2010 - Magic ^& Mayhem (320)\12 - My Kantele.mp3"
set "FileName=12 - My Kantele.mp3"
set "ShortFileName=12 - My Kantele"

ECHO FileName: !FileName!
ECHO ShortFileName: !ShortFileName!

set OutDir=!OutLoc!
call set OutDir=%%OutDir:!FileName!=%%
call set OutLoc=%%OutLoc:!FileName!=%%!!ShortFileName!.mp3

ECHO OutDir: !OutDir!
ECHO OutLoc: !OutLoc!

@REM [...]

例如替换&^&short-dummy.bat输出)

FileName: 12 - My Kantele.mp3
ShortFileName: 12 - My Kantele
OutDir: K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Magic & Mayhem (320)\2010 - Magic & Mayhem (320)\
OutLoc: K:\Tools\Video Tools\ffmpeg-4.3.1-win64-static\output\Amorphis - Discographie 1992-2013\2010 - Magic & Mayhem (320)\2010 - Magic & Mayhem (320)\12 - My Kantele.mp3

相关内容