我可能已经把自己逼到了一个角落,但由于某种原因,这批处理几乎可以处理任何名称,除了带有
!
的名称。我一生都无法弄清楚为什么我做错了。
如果文件名称中包含 !
,它将忽略它并创建一个不带 !
的新文件,并将其移动到批处理目录中。
实际上,该文件名的每个实例最终都会缺少 !
,甚至输出 MGL 中的引号:
setlocal enabledelayedexpansion
set choice=
set /p choice= Type selection and press enter:
if '%choice%'=='1' set console=SNES
if '%choice%'=='1' set settings=delay="2" type="f" index="0"
if '%choice%'=='2' set console=PSX
if '%choice%'=='2' set settings=delay="1" type="s" index="0"
if '%choice%'=='3' set console=gameboy
if '%choice%'=='3' set settings=delay="1" type="f" index="1"
if '%choice%'=='4' set console=C64
if '%choice%'=='4' set settings=delay="1" type="f" index="1"
mkdir MGL_!console! > nul 2>&1
for /R %%a in (*.32x,*.a26,*.a78,*.abs,*.bin,*.bs,*.chd,*.cof,*.col,*.fds,*.gb,*.gbc,*.gba,*.gg,*.j64,*.jag,*.lnx,*.md,*.neo,*.nes,*.o,*.pce,*.rom,*.sc,*.sfc,*.sg,*.smc,*.smd,*.sms,"*.vec",*.wsc,*.ws) do (
set "filepath=%%a"
set "filepath=!filepath:%CD%=!"
set "filepath=!filepath:\=/!"
set "filepath=!filepath:~1!"
echo ^<mistergamedescription^> > "%%~pna.mgl"
echo ^<rbf^>_console/!console!^</rbf^> >> "%%~pna.mgl"
echo ^<file !settings! path="!filepath!"/^> >> "%%~pna.mgl"
echo ^</mistergamedescription^> >> "%%~pna.mgl"
>nul move "%%~pna.mgl" "MGL_%console%\%%~na.mgl"
)
输出看起来全程都不错?
echo </mistergamedescription> 1>>"\mISTER\Done\Vectrex\1 World - A-Z\Blitz! - Action Football (USA, Europe) (0F11CE0C).mgl"
move "\mISTER\Done\Vectrex\1 World - A-Z\Blitz! - Action Football (USA, Europe) (0F11CE0C).mgl" "MGL_!console!\Blitz! - Action Football (USA, Europe) (0F11CE0C).mgl" 1>nul
)
The system cannot find the file specified.
我认为该问题是由于在
%%a
扩展期间启用了延迟扩展而引起的,因为受影响的路径(\Blitz! - *
)中有一个感叹号,该路径由于延迟扩展而丢失。因此,您需要切换延迟扩展,以便仅在实际需要时启用它。这是一种改进的方法,还具有其他一些改进:
rem // Initially disable delayed expansion:
setlocal DisableDelayedExpansion
:USER_PROMPT
rem // Use quoted `set` syntax in general:
set "choice="
set /P choice="Type select and press enter: "
rem /* Use quotation marks rather than apostrophes (single quotes);
rem then combine multiple duplicate conditions using the `&` operator;
rem unwanted trailing spaces are prevented by the quoted `set` syntax: */
if "%choice%"=="1" set "console=SNES" & set settings=delay="2" type="f" index="0"
if "%choice%"=="2" set "console=PSX" & set settings=delay="1" type="s" index="0"
if "%choice%"=="3" set "console=gameboy" & set settings=delay="1" type="f" index="1"
if "%choice%"=="4" set "console=C64" & set settings=delay="1" type="f" index="1"
rem // Retry if user entered something else:
goto :USER_PROMPT
::rem /* Alternative user prompt approach -- the `choice` command;
::rem the `ErrorLevel` variable reflects the POSITION of the choices;
::rem it is just coincidental here that its value equals the choice: */
::choice /C 1234 /M "Type selection: " /N
::if %ErrorLevel% equ 1 set "console=SNES" & set settings=delay="2" type="f" index="0"
::if %ErrorLevel% equ 2 set "console=PSX" & set settings=delay="1" type="s" index="0"
::if %ErrorLevel% equ 3 set "console=gameboy" & set settings=delay="1" type="f" index="1"
::if %ErrorLevel% equ 4 set "console=C64" & set settings=delay="1" type="f" index="1"
::goto :EOF
mkdir "MGL_%console%" > nul 2>&1
for /R %%a in (*.32x,*.a26,*.a78,*.abs,*.bin,*.bs,*.chd,*.cof,*.col,*.fds,*.gb,*.gbc,*.gba,*.gg,*.j64,*.jag,*.lnx,*.md,*.neo,*.nes,*.o,*.pce,*.rom,*.sc,*.sfc,*.sg,*.smc,*.smd,*.sms,"*.vec",*.wsc,*.ws) do (
rem // Delayed expansion is still disabled at this point, so it does not interfere with expansion of `%%a`:
set "filepath=%%~a"
rem // Immediately set target file, hence no more move is necessary later:
set "targetfile=MGL_%console%\%%~na.mgl"
rem // Now toggle delayed expansion:
setlocal EnableDelayedExpansion
set "filepath=!filepath:*%CD%\=!"
::rem // Improved variant to remove root path, avoiding immediate (`%`-)expansion:
::for /F "delims=" %%b in ("!CD!") do set "filepath=!filepath:*%%b\=!"
set "filepath=!filepath:\=/!"
rem // Redirect to the target file only once (also avoiding trailing spaces):
> "!targetfile!" (
echo ^<mistergamedescription^>
echo ^<rbf^>_console/!console!^</rbf^>
echo ^<file !settings! path="!filepath!"/^>
echo ^</mistergamedescription^>
)
endlocal
)
endlocal
无论如何,我仍然不明白你是否真的想在每次
*.mgl
循环迭代中覆盖 for /R
文件。
文件名包含一个或多个
!
的问题是由于在执行命令行 set "filepath=%%a"
时启用延迟扩展而导致的。该命令行被第二次解析,因为启用了延迟扩展,导致将分配给循环变量 a
的文件名中的所有感叹号解释为延迟扩展变量引用的开始/结束。因此,在将剩余字符串分配给环境变量filepath
之前,会从文件名字符串中删除单个感叹号。
这是重写的代码,以避免这个问题和其他几个可能的问题。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
cls
echo(
echo Select a system to create MGL files for:
echo(
echo 1. SNES
echo 2. PSX
echo 3. Gameboy
echo 4. C64
echo(
:Console0
%SystemRoot%\System32\choice.exe /C 1234 /N /M "Please make your selection:"
goto Console%ErrorLevel%
:Console1
set "console=SNES"
set "settings=delay="2" type="f" index="0""
goto CreateFolder
:Console2
set "console=PSX"
set "settings=delay="1" type="s" index="0""
goto CreateFolder
:Console3
set "console=gameboy"
set "settings=delay="1" type="f" index="1""
goto CreateFolder
:Console4
set "console=C64"
set "settings=delay="1" type="f" index="1""
:CreateFolder
mkdir "MLG_%console%" 2>nul
if exist "MLG_%console%\" goto GetBasePathLength
for %%I in (".\MLG_%console%") do echo ERROR: Failed to create the directory: "%%~fI"
echo(
@setlocal EnableExtensions EnableDelayedExpansion & for /F "tokens=1,2" %%G in ("!CMDCMDLINE!") do @endlocal & if /I "%%~nG" == "cmd" if /I "%%~H" == "/c" pause
goto EndBatch
:GetBasePathLength
set "BasePath=_%CD%"
if not "%BasePath:~-1%" == "\" set "BasePath=%BasePath%\"
setlocal EnableDelayedExpansion
set "BasePathLength=0"
for /L %%I in (12,-1,0) do (
set /A "BasePathLength|=1<<%%I"
for %%J in (!BasePathLength!) do if "!BasePath:~%%J,1!" == "" set /A "BasePathLength&=~1<<%%I"
)
endlocal & set "BasePathLength=%BasePathLength%"
for /R %%I in (*.32x,*.a26,*.a78,*.abs,*.bin,*.bs,*.chd,*.cof,*.col,*.fds,*.gb,*.gbc,*.gba,*.gg,*.j64,*.jag,*.lnx,*.md,*.neo,*.nes,*.o,*.pce,*.rom,*.sc,*.sfc,*.sg,*.smc,*.smd,*.sms,"*.vec",*.wsc,*.ws) do (
set "FileName=%%I"
set "OutputFile=MLG_%console%\%%~nI.mgl"
setlocal EnableDelayedExpansion
set "FileName=!FileName:~%BasePathLength%!"
set "FileName=!FileName:\=/!"
(
echo ^<mistergamedescription^>
echo ^<rbf^>_console/!console!^</rbf^>
echo ^<file !settings! path="!FileName!"/^>
echo ^</mistergamedescription^>
)>"!OutputFile!"
endlocal
)
:EndBatch
endlocal
我建议先阅读:
set /P
的原因。set "variable=value"
定义环境变量的原因。看起来奇怪的 FOR 命令行,末尾带有命令
pause
,仅当处理批处理文件的 Windows 命令处理器实例启动且第一个参数不区分大小写 /C
时才运行 PAUSE,如双击时完成的那样在批处理文件上。如果首先打开命令提示符窗口,然后在命令提示符窗口中执行批处理文件,并且在当前目录中创建输出目录时发生错误,则不会执行PAUSE,在这种情况下,可以读取错误消息命令提示符窗口而不使用
pause
。
主 FOR 循环首先将完全限定的文件名分配给环境变量
FileName
,同时禁用延迟扩展以处理路径/名称中带有一个或多个问号的正确文件名。
相对于当前目录的路径的输出文件名也被分配给环境变量
OutputFile
,同时禁用延迟扩展也适用于像Blitz! - Action Football (USA, Europe) (0F11CE0C).mgl
这样的文件名。
启用下一个延迟扩展,这会导致在后台执行其他操作,如此答案中所述,其中包含有关命令SETLOCAL和ENDLOCAL的所有详细信息。
然后从文件名中删除基本路径,并使用延迟扩展将所有
\
替换为 /
。
接下来,创建并打开输出文件,使用延迟扩展将 ECHO 的四行输出写入文件中。最后关闭输出文件并使用命令ENDLOCAL恢复之前的执行环境。
要了解所使用的命令及其工作原理,请打开命令提示符窗口,执行以下命令,并完整、仔细地阅读每个命令显示的帮助页面。
choice /?
cls /?
echo /?
endlocal /?
for /?
goto /?
if /?
mkdir /?
pause /?
set /?
setlocal /?