批处理文件数学(减法)停止工作
Batch File Math (Subtraction) Stopped Working
我知道我这边肯定有一些语法问题,但我现在无法解决这个问题。这是我几个月前整理的一个脚本,可以在每月发布时自动为我下载一些文件。它工作正常,但是这个月我注意到没有任何下载。经过一些故障排除和调查,我发现月份变量将其自身设置为“-1”,而不是从其当前值中减去“1”。因此,我无法获得正确的文件名来尝试下载。这与我在天数、修订号和年份上使用的匹配相同,但出于某种原因,月份变量与我不合作,我不明白为什么。
:MASTER
@echo off
mode con:cols=100 lines=5
::Setup First Download
for /f "tokens=2*" %%a in ('REG Query "HKLM\SOFTWARE\Wow6432Node\ExampleRegistryKey" /v ExampleString 2^>nul') do set "ExampleDir=%%~b"
pushd "%ExampleDir%"
for /f "tokens=2 delims==" %%a in ('findstr SQLiteHome Example.ini') do set SQLiteHome=%%a
::Revision Number
set num=17
set /a "num=num-1"
::Begin set date
for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date
:set_date
if "%1:~0,1%" gtr "9" shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof
:end_set_date
set dd=31
这里的这一部分是月份开始等于 -1 而不是从当前月份减去当前月份的地方。
set /a "mm=mm-1"
if %mm%==9 set mm=09
if %mm%==8 set mm=08
if %mm%==7 set mm=07
if %mm%==6 set mm=06
if %mm%==5 set mm=05
if %mm%==4 set mm=04
if %mm%==3 set mm=03
if %mm%==2 set mm=02
if %mm%==1 set mm=01
if %mm%==0 set /a "yy=yy-1"
if %mm%==0 set mm=12
Set EarlyEntry=early_up_sqlite_r
Set FullEntry=update_sqlite_r
Set MstarEntry=mstar_ext_sqlite_r
Set PSNEntry=psn_ext_sqlite_r
set CurrentEntry=%EarlyEntry%
set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
ECHO %ThisFile%
set INIentry=EarlyLast
::Create %WorkingDirectory%\Dates.txt and Downloads.ini
set INIfile=C:\ProgramData\SA_Updater\Downloads.ini
set WorkingDirectory=C:\ProgramData\SA_Updater\
set OldDownloads=C:\ProgramData\SA_Updater\Downloads\
set PSNini=PSNLast
set MSTARini=MSTARLast
set Fullini=FullLast
set Earlyini=EarlyLast
echo %PSNini%
echo Dates > %WorkingDirectory%\Dates.txt
if not exist "%WorkingDirectory%" mkdir "%WorkingDirectory%"
if not exist "%OldDownloads%" mkdir "%OldDownloads%"
pusd %WorkingDirectory%
if not exist "%INIfile%" (
ECHO %PSNini%= > %INIfile%
ECHO %MSTARini%= >> %INIfile%
ECHO %Fullini%= >> %INIfile%
ECHO %Earlyini%= >> %INIfile%
)
:ObtainVariables
::Find the Last version downloaded
这是我保存下载尝试日志的地方
echo 日期 > %WorkingDirectory%\Dates.txt
输出看起来像这样:
Dates
http://Example.com/updates/early_up_sqlite_r16_2018-131.exe
http://Example.com/updates/early_up_sqlite_r15_2018-131.exe
http://Example.com/updates/early_up_sqlite_r14_2018-131.exe
而不是:
Dates
http://Example.com/updates/early_up_sqlite_r16_20180731.exe
http://Example.com/updates/early_up_sqlite_r15_20180731.exe
http://Example.com/updates/early_up_sqlite_r14_20180731.exe
脚本的其余部分:
for /f "tokens=2 delims==" %%a in ('findstr %INIentry% %INIfile%') do set LastINIfile=%%a
ECHO %LastINIfile%
::Revision Number
set num=17
set /a "num=num-1"
::Begin set date
for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date
:set_date
if "%1:~0,1%" gtr "9" shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof
:end_set_date
set dd=31
set /a "mm=mm-1"
if %mm%==9 set mm=09
if %mm%==8 set mm=08
if %mm%==7 set mm=07
if %mm%==6 set mm=06
if %mm%==5 set mm=05
if %mm%==4 set mm=04
if %mm%==3 set mm=03
if %mm%==2 set mm=02
if %mm%==1 set mm=01
if %mm%==0 set /a "yy=yy-1"
if %mm%==0 set mm=12
set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
goto Download
:Download
::Setup Download Variables
cls
set Download=http://Example.com/updates/%ThisFile%
GOTO TryDownload
:TryDownload
::Add Download Attempt to %WorkingDirectory%\Dates.txt
echo %Download% >> %WorkingDirectory%\Dates.txt
::Is the file download? If so, start the install
cls
mode con:cols=100 lines=10
powershell "Import-Module BitsTransfer; Start-BitsTransfer '%Download%' '%DownloadDir%'"
mode con:cols=100 lines=5
cls
::ping localhost -n 3 >nul
if exist C:\ProgramData\SA_Updater\%ThisFile% (
GOTO StartInstall
) else (
::Try Downloading
GOTO TryAgain
)
cls
:TryAgain
set /a "num=num-1"
if %mm%==9 set mm=09
if %mm%==8 set mm=08
if %mm%==7 set mm=07
if %mm%==6 set mm=06
if %mm%==5 set mm=05
if %mm%==4 set mm=04
if %mm%==3 set mm=03
if %mm%==2 set mm=02
if %mm%==1 set mm=01
if %num%==0 set /a "dd=dd-1"
if %dd%==27 set /a "mm=mm-1"
if %mm%==0 set /a "yy=yy-1"
if %mm%==0 set mm=12
if %dd%==27 set dd=33
if %num%==0 set num=17
set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
set Download=http://Example.com/updates/%ThisFile%
set DownloadDir=%WorkingDirectory%%ThisFile%
if %ThisFile% EQU %LastINIfile% GOTO CheckEarly
GOTO TryDownload
)
:StartInstall
%WorkingDirectory%%ThisFile% /w /v"INSTALLPREREQUISITES=0"
robocopy %WorkingDirectory% %OldDownloads% %ThisFile%
pushd %WorkingDirectory%
del %ThisFile%
del /Q /A H *.tmp
GOTO DoINIstuff
::Update ThisFile
:CheckEarly
if %CurrentEntry% EQU %EarlyEntry% GOTO SetupFull
GOTO CheckFull
:CheckFull
if %CurrentEntry% EQU %FullEntry% GOTO SetupMstar
GOTO CheckMstar
:CheckMstar
if %CurrentEntry% EQU %MstarEntry% GOTO SetupPSN
GOTO CheckPSN
:CheckPSN
if %CurrentEntry% EQU %PSNEntry% GOTO SetupEarly
GOTO CheckEarly
::Setups
:SetupFull
set CurrentEntry=%FullEntry%
set INIentry=%Fullini%
GOTO ObtainVariables
:SetupMstar
set CurrentEntry=%MstarEntry%
set INIentry=%MSTARini%
GOTO ObtainVariables
:SetupPSN
set CurrentEntry=%PSNEntry%
set INIentry=%PSNini%
GOTO ObtainVariables
:SetupEarly
set CurrentEntry=%EarlyEntry%
set INIentry=%Earlyini%
GOTO ObtainVariables
:DoINIstuff
SetLocal EnableDelayedExpansion
Set _PathtoFile=%INIfile%
Set _OldLine=%INIentry%=
Set _NewLine=%INIentry%=%ThisFile%
Call :_Parse "%_PathtoFile%"
Set _Len=0
Set _Str=%_OldLine%
Set _Str=%_Str:"=.%987654321
:_Loop
If NOT "%_Str:~18%"=="" Set _Str=%_Str:~9%& Set /A _Len+=9& Goto _Loop
Set _Num=%_Str:~9,1%
Set /A _Len=_Len+_Num
PushD %_FilePath%
If Exist %_FileName%.new Del %_FileName%.new
If Exist %_FileName%.old Del %_FileName%.old
Set _LineNo=0
For /F "Tokens=* Eol=" %%I In (%_FileName%%_FileExt%) Do (
Set _tmp=%%I
Set /A _LineNo+=1
If /I "!_tmp:~0,%_Len%!"=="%_OldLine%" (
>>%_FileName%.new Echo %_NewLine%
) Else (
If !_LineNo! GTR 1 If "!_tmp:~0,1!"=="[" Echo.>>%_FileName%.new
SetLocal DisableDelayedExpansion
>>%_FileName%.new Echo %%I
EndLocal
))
Ren %_FileName%%_FileExt% %_FileName%.old
Ren %_FileName%.new %_FileName%.ini
PopD
Goto :CheckEarly
:_Parse
Set _FilePath=%~dp1
Set _FileName=%~n1
Set _FileExt=%~x1
Goto :EOF
我希望我没有遗漏太多来解决这个问题。基本上,我可以看到修订号和日期按它们应有的方式运行,但是月份只是恢复为“-1”而不是实际执行减法,我基本上是在竭尽全力想弄清楚我的意思我做错了。
原因很简单。带有前导 0
的数字字符串被 C 函数 strtol 解释为八进制数,cmd.exe
使用该函数将数字字符串转换为整数。
08
和 09
是 octal numeral system 中的无效数字,因此函数 strtol
returns 0
接下来减去1
导致 -1
.
简单的解决方案是使用 set /a "mm=1%mm%-101"
而不是 set /a "mm=mm-1"
。然后首先将月份值作为字符串与字符 1
连接起来,构建字符串 101
到 112
,因此数字字符串不再有前导 0
并且从这个数字 101
被减去得到 0
到 11
作为字符串分配给环境变量 mm
.
顺便说一句:使用以下两个命令行来取回减法后的前导零:
set "mm=0%mm%"
set "mm=%mm:~-2%"
第一行将 0
-11
连接到 00
-011
,第二行仅采用该字符串的最后两个字符,结果为 00
-11
最终赋值给环境变量mm
.
接下来的两行应替换为:if %mm% == 00 set "mm=12" & set /a "yy-=1"
我知道我这边肯定有一些语法问题,但我现在无法解决这个问题。这是我几个月前整理的一个脚本,可以在每月发布时自动为我下载一些文件。它工作正常,但是这个月我注意到没有任何下载。经过一些故障排除和调查,我发现月份变量将其自身设置为“-1”,而不是从其当前值中减去“1”。因此,我无法获得正确的文件名来尝试下载。这与我在天数、修订号和年份上使用的匹配相同,但出于某种原因,月份变量与我不合作,我不明白为什么。
:MASTER
@echo off
mode con:cols=100 lines=5
::Setup First Download
for /f "tokens=2*" %%a in ('REG Query "HKLM\SOFTWARE\Wow6432Node\ExampleRegistryKey" /v ExampleString 2^>nul') do set "ExampleDir=%%~b"
pushd "%ExampleDir%"
for /f "tokens=2 delims==" %%a in ('findstr SQLiteHome Example.ini') do set SQLiteHome=%%a
::Revision Number
set num=17
set /a "num=num-1"
::Begin set date
for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date
:set_date
if "%1:~0,1%" gtr "9" shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof
:end_set_date
set dd=31
这里的这一部分是月份开始等于 -1 而不是从当前月份减去当前月份的地方。
set /a "mm=mm-1"
if %mm%==9 set mm=09
if %mm%==8 set mm=08
if %mm%==7 set mm=07
if %mm%==6 set mm=06
if %mm%==5 set mm=05
if %mm%==4 set mm=04
if %mm%==3 set mm=03
if %mm%==2 set mm=02
if %mm%==1 set mm=01
if %mm%==0 set /a "yy=yy-1"
if %mm%==0 set mm=12
Set EarlyEntry=early_up_sqlite_r
Set FullEntry=update_sqlite_r
Set MstarEntry=mstar_ext_sqlite_r
Set PSNEntry=psn_ext_sqlite_r
set CurrentEntry=%EarlyEntry%
set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
ECHO %ThisFile%
set INIentry=EarlyLast
::Create %WorkingDirectory%\Dates.txt and Downloads.ini
set INIfile=C:\ProgramData\SA_Updater\Downloads.ini
set WorkingDirectory=C:\ProgramData\SA_Updater\
set OldDownloads=C:\ProgramData\SA_Updater\Downloads\
set PSNini=PSNLast
set MSTARini=MSTARLast
set Fullini=FullLast
set Earlyini=EarlyLast
echo %PSNini%
echo Dates > %WorkingDirectory%\Dates.txt
if not exist "%WorkingDirectory%" mkdir "%WorkingDirectory%"
if not exist "%OldDownloads%" mkdir "%OldDownloads%"
pusd %WorkingDirectory%
if not exist "%INIfile%" (
ECHO %PSNini%= > %INIfile%
ECHO %MSTARini%= >> %INIfile%
ECHO %Fullini%= >> %INIfile%
ECHO %Earlyini%= >> %INIfile%
)
:ObtainVariables
::Find the Last version downloaded
这是我保存下载尝试日志的地方 echo 日期 > %WorkingDirectory%\Dates.txt 输出看起来像这样:
Dates
http://Example.com/updates/early_up_sqlite_r16_2018-131.exe
http://Example.com/updates/early_up_sqlite_r15_2018-131.exe
http://Example.com/updates/early_up_sqlite_r14_2018-131.exe
而不是:
Dates
http://Example.com/updates/early_up_sqlite_r16_20180731.exe
http://Example.com/updates/early_up_sqlite_r15_20180731.exe
http://Example.com/updates/early_up_sqlite_r14_20180731.exe
脚本的其余部分:
for /f "tokens=2 delims==" %%a in ('findstr %INIentry% %INIfile%') do set LastINIfile=%%a
ECHO %LastINIfile%
::Revision Number
set num=17
set /a "num=num-1"
::Begin set date
for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date
:set_date
if "%1:~0,1%" gtr "9" shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof
:end_set_date
set dd=31
set /a "mm=mm-1"
if %mm%==9 set mm=09
if %mm%==8 set mm=08
if %mm%==7 set mm=07
if %mm%==6 set mm=06
if %mm%==5 set mm=05
if %mm%==4 set mm=04
if %mm%==3 set mm=03
if %mm%==2 set mm=02
if %mm%==1 set mm=01
if %mm%==0 set /a "yy=yy-1"
if %mm%==0 set mm=12
set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
goto Download
:Download
::Setup Download Variables
cls
set Download=http://Example.com/updates/%ThisFile%
GOTO TryDownload
:TryDownload
::Add Download Attempt to %WorkingDirectory%\Dates.txt
echo %Download% >> %WorkingDirectory%\Dates.txt
::Is the file download? If so, start the install
cls
mode con:cols=100 lines=10
powershell "Import-Module BitsTransfer; Start-BitsTransfer '%Download%' '%DownloadDir%'"
mode con:cols=100 lines=5
cls
::ping localhost -n 3 >nul
if exist C:\ProgramData\SA_Updater\%ThisFile% (
GOTO StartInstall
) else (
::Try Downloading
GOTO TryAgain
)
cls
:TryAgain
set /a "num=num-1"
if %mm%==9 set mm=09
if %mm%==8 set mm=08
if %mm%==7 set mm=07
if %mm%==6 set mm=06
if %mm%==5 set mm=05
if %mm%==4 set mm=04
if %mm%==3 set mm=03
if %mm%==2 set mm=02
if %mm%==1 set mm=01
if %num%==0 set /a "dd=dd-1"
if %dd%==27 set /a "mm=mm-1"
if %mm%==0 set /a "yy=yy-1"
if %mm%==0 set mm=12
if %dd%==27 set dd=33
if %num%==0 set num=17
set ThisFile=%CurrentEntry%%num%_%yy%%mm%%dd%.exe
set Download=http://Example.com/updates/%ThisFile%
set DownloadDir=%WorkingDirectory%%ThisFile%
if %ThisFile% EQU %LastINIfile% GOTO CheckEarly
GOTO TryDownload
)
:StartInstall
%WorkingDirectory%%ThisFile% /w /v"INSTALLPREREQUISITES=0"
robocopy %WorkingDirectory% %OldDownloads% %ThisFile%
pushd %WorkingDirectory%
del %ThisFile%
del /Q /A H *.tmp
GOTO DoINIstuff
::Update ThisFile
:CheckEarly
if %CurrentEntry% EQU %EarlyEntry% GOTO SetupFull
GOTO CheckFull
:CheckFull
if %CurrentEntry% EQU %FullEntry% GOTO SetupMstar
GOTO CheckMstar
:CheckMstar
if %CurrentEntry% EQU %MstarEntry% GOTO SetupPSN
GOTO CheckPSN
:CheckPSN
if %CurrentEntry% EQU %PSNEntry% GOTO SetupEarly
GOTO CheckEarly
::Setups
:SetupFull
set CurrentEntry=%FullEntry%
set INIentry=%Fullini%
GOTO ObtainVariables
:SetupMstar
set CurrentEntry=%MstarEntry%
set INIentry=%MSTARini%
GOTO ObtainVariables
:SetupPSN
set CurrentEntry=%PSNEntry%
set INIentry=%PSNini%
GOTO ObtainVariables
:SetupEarly
set CurrentEntry=%EarlyEntry%
set INIentry=%Earlyini%
GOTO ObtainVariables
:DoINIstuff
SetLocal EnableDelayedExpansion
Set _PathtoFile=%INIfile%
Set _OldLine=%INIentry%=
Set _NewLine=%INIentry%=%ThisFile%
Call :_Parse "%_PathtoFile%"
Set _Len=0
Set _Str=%_OldLine%
Set _Str=%_Str:"=.%987654321
:_Loop
If NOT "%_Str:~18%"=="" Set _Str=%_Str:~9%& Set /A _Len+=9& Goto _Loop
Set _Num=%_Str:~9,1%
Set /A _Len=_Len+_Num
PushD %_FilePath%
If Exist %_FileName%.new Del %_FileName%.new
If Exist %_FileName%.old Del %_FileName%.old
Set _LineNo=0
For /F "Tokens=* Eol=" %%I In (%_FileName%%_FileExt%) Do (
Set _tmp=%%I
Set /A _LineNo+=1
If /I "!_tmp:~0,%_Len%!"=="%_OldLine%" (
>>%_FileName%.new Echo %_NewLine%
) Else (
If !_LineNo! GTR 1 If "!_tmp:~0,1!"=="[" Echo.>>%_FileName%.new
SetLocal DisableDelayedExpansion
>>%_FileName%.new Echo %%I
EndLocal
))
Ren %_FileName%%_FileExt% %_FileName%.old
Ren %_FileName%.new %_FileName%.ini
PopD
Goto :CheckEarly
:_Parse
Set _FilePath=%~dp1
Set _FileName=%~n1
Set _FileExt=%~x1
Goto :EOF
我希望我没有遗漏太多来解决这个问题。基本上,我可以看到修订号和日期按它们应有的方式运行,但是月份只是恢复为“-1”而不是实际执行减法,我基本上是在竭尽全力想弄清楚我的意思我做错了。
原因很简单。带有前导 0
的数字字符串被 C 函数 strtol 解释为八进制数,cmd.exe
使用该函数将数字字符串转换为整数。
08
和 09
是 octal numeral system 中的无效数字,因此函数 strtol
returns 0
接下来减去1
导致 -1
.
简单的解决方案是使用 set /a "mm=1%mm%-101"
而不是 set /a "mm=mm-1"
。然后首先将月份值作为字符串与字符 1
连接起来,构建字符串 101
到 112
,因此数字字符串不再有前导 0
并且从这个数字 101
被减去得到 0
到 11
作为字符串分配给环境变量 mm
.
顺便说一句:使用以下两个命令行来取回减法后的前导零:
set "mm=0%mm%"
set "mm=%mm:~-2%"
第一行将 0
-11
连接到 00
-011
,第二行仅采用该字符串的最后两个字符,结果为 00
-11
最终赋值给环境变量mm
.
接下来的两行应替换为:if %mm% == 00 set "mm=12" & set /a "yy-=1"