运行时更改batch file

我正在运行一个长期运行的batch file。 我现在意识到,我必须在batch file的末尾添加一些更多的命令(不更改现有内容,只是一些额外的命令)。 是否有可能这样做,因为大多数batch file是逐步读取和逐个执行? 或者系统读取文件的全部内容,然后运行该作业?

我只是试了一下,并且违背了我的直觉,在最后(在Windows XP上)拿起了新的命令,

我创建了一个批处理文件

echo Hello pause echo world 

我运行了该文件,并暂停,添加

 echo Salute 

保存并按下Enter键以控制暂停,所有三个提示都被回显到控制台。

所以,去吧!

命令解释器会记住批处理文件中的行位置。 只要你在当前执行的行位置之后修改这个批处理文件就可以了。

如果你之前修改它会开始做奇怪的事情(重复命令等)。

jeb的例子很有趣,但是它非常依赖于添加或删除的文本的长度。 我认为反直觉的结果是什么意思,当他说:“如果你之前修改它会开始做奇怪的事情(重复命令等)”。

我已经修改了jeb的代码,以显示如何在执行的批处理文件的开始处自由修改不同长度的动态代码,只要适当的填充到位。 整个动态部分被每次迭代完全替换。 每条动态线都以非干扰为前缀; 这可以方便地让FOR /F去掉动态代码,因为隐含的EOL=; 选项。

我不寻找一个特定的行号,而是寻找一个特定的评论来定位动态代码的开始。 这很容易维护。

我使用等号的线来无害地填充代码以允许扩展和收缩。 可以使用以下字符的任意组合:逗号,分号,相等,空格,制表符和/或换行符。 (当然,填充不能以分号开始。)圆括号内的等号允许代码扩展。 括号后的等号允许代码收缩。

请注意FOR /F去掉空行。 这个限制可以通过使用FINDSTR在每行的前面加上行号来解决,然后在循环中去掉前缀。 但是额外的代码会减慢速度,所以除非代码依赖空白行,否则不值得。

 @echo off setlocal DisableDelayedExpansion echo The starting filesize is %~z0 :loop echo ---------------------- ::*** Start of dynamic code *** ;set value=1 ::*** End of dynamic code *** echo The current value=%value% :: ::The 2 lines of equal signs amount to 164 bytes, including end of line chars. ::Putting the lines both within and after the parentheses allows for expansion ::or contraction by up to 164 bytes within the dynamic section of code. ( call :changeBatch ============================================================================== ============================================================================== ) ================================================================================ ================================================================================ set /p "quit=Enter Q to quit, anything else to continue: " if /i "%quit%"=="Q" exit /b goto :loop :changeBatch ( for /f "usebackq delims=" %%a in ("%~f0") do ( echo %%a if "%%a"=="::*** Start of dynamic code ***" ( setlocal enableDelayedExpansion set /a newValue=value+1, extra=!random!%%9 echo ;set value=!newValue! for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n endlocal ) ) ) >"%~f0.tmp" :: ::The 2 lines of equal signs amount to 164 bytes, including end of line chars. ::Putting the lines both within and after the parentheses allows for expansion ::or contraction by up to 164 bytes within the dynamic section of code. ( move /y "%~f0.tmp" "%~f0" > nul ============================================================================== ============================================================================== ) ================================================================================ ================================================================================ echo The new filesize is %~z0 exit /b 

上面的工作,但是如果动态代码移动到文件末尾的子例程,事情会变得更加容易。 代码可以无限制地扩展和收缩,而不需要填充。 在去除动态部分的时候FINDSTR比FOR / F要快得多。 动态行可以安全地以分号(包括标签!)作为前缀。 然后使用FINDSTR / V选项排除以分号开始的行,并且可以简单地添加新的动态代码。

 @echo off setlocal DisableDelayedExpansion echo The starting filesize is %~z0 :loop echo ---------------------- call :dynamicCode1 call :dynamicCode2 echo The current value=%value% call :changeBatch set /p "quit=Enter Q to quit, anything else to continue: " if /i "%quit%"=="Q" exit /b goto :loop :changeBatch ( findstr /v "^;" "%~f0" setlocal enableDelayedExpansion set /a newValue=value+1, extra=!random!%%9 echo ;:dynamicCode1 echo ;set value=!newValue! echo ;exit /b echo ; echo ;:dynamicCode2 for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n echo ;exit /b endlocal ) >"%~f0.tmp" move /y "%~f0.tmp" "%~f0" > nul echo The new filesize is %~z0 exit /b ;:dynamicCode1 ;set value=33 ;exit /b ; ;:dynamicCode2 ;echo extra line 1 ;exit /b 

几乎像rein说的那样,cmd.exe记住它当前的文件位置(不仅是行位置),而且对于每个调用,它都将文件位置推到不可见的堆栈上。

这意味着,你可以编辑你的文件,当它运行在实际文件位置后面时,你只需要你做什么…

一个自修改批次的小样本
它连续更改行set value=1000

 @echo off setlocal DisableDelayedExpansion :loop REM **** the next line will be changed set value=1000 rem *** echo ---------------------- echo The current value=%value% <nul set /p ".=Press a key" pause > nul echo( ( call :changeBatch rem This should be here and it should be long ) rem ** It is neccessary, that this is also here! goto :loop rem ... :changeBatch set /an=0 set /a newValue=value+1 set /a toggle=value %% 2 set "theNewLine=set value=%newValue%" if %toggle%==0 ( set "theNewLine=%theNewLine% & rem This adds 50 byte to the filesize.........." ) del "%~f0.tmp" 2> nul for /F "usebackq delims=" %%a in ("%~f0") DO ( set /a n+=1 set "line=%%a" setlocal EnableDelayedExpansion if !n!==5 ( (echo !theNewLine!) ) ELSE ( (echo !line!) ) endlocal ) >> "%~f0.tmp" ( rem the copy should be done in a parenthesis block copy "%~f0.tmp" "%~f0" > nul if Armageddon==TheEndOfDays ( echo This can't never be true, or is it? ) ) echo The first line after the replace action.... echo The second line comes always after the first line? echo The current filesize is now %~z0 goto :eof 

简短的回答:是的,批处理文件可以在运行时自行修改。 正如其他人已经证实。

几年前,在Windows 3之前,我工作的地方有一个MS-DOS的内部菜单系统。 它运行的方式非常优雅:它实际上是从一个批处理文件运行的,主程序(用C编写)被修改以运行脚本。 这个窍门意味着菜单程序本身在选择运行时并不占用内存空间。 这包括诸如LAN Mail程序和3270终端程序之类的东西。

但是从一个自修改的批处理文件运行,意味着它的脚本也可以执行加载TSR程序的事情,事实上可以做任何你可以放在批处理文件中的东西。 这使得它非常强大。 只有GOTO命令不起作用,直到作者最终想出了如何使批处理文件为每个命令重新启动。

命令解释程序似乎记住了每个正在读取的命令文件中的字节偏移量,但是文件本身并未锁定,所以可以在运行时使用文本编辑器进行更改。

如果在记住的位置之后对文件进行了改变,解释者应该继续执行现在修改的脚本。 但是,如果在该点之前进行了更改,并且该修改会更改此时文本的长度(例如,您插入或删除了一些文本),则记住的位置现在不再指下一个命令的开始。 当解释器试图读取下一个“行”时,它会取而代之选择不同的行,或者可能是行的一部分,这取决于插入或删除多少文本。 如果幸运的话,它可能无法处理所发生的任何事情,出现错误,并继续从下一行开始执行,但仍可能不是您想要的。

但是,通过了解正在发生的事情,您可以构建脚本以降低风险。 我有脚本实现简单的菜单系统,通过显示菜单,接受用户使用choice命令的输入,然后处理选择。 诀窍是确保脚本等待输入的位置靠近文件的顶部,以便您可能希望进行的任何编辑都会在该位置之后发生,因此不会产生恶劣的影响。

例:

 :top call :displayMenu :prompt REM The script will spend most of its time waiting here. choice /C:1234 /N "Enter selection: " if ERRORLEVEL == 4 goto DoOption4 if ERRORLEVEL == 3 goto DoOption3 if ERRORLEVEL == 2 goto DoOption2 goto DoOption1 :displayMenu (many lines to display menu) goto prompt :DoOption1 (many lines to do Option 1) goto top :DoOption2 (many lines to do Option 2) goto top (etc)