我正在运行一个长时间运行的批处理文件。我现在意识到我必须在批处理文件的末尾添加更多命令(不需要更改现有内容,只需要一些额外的命令)。是否可以这样做,因为大多数批处理文件是逐步读取并逐个执行的?或者系统是否读取文件的全部内容然后运行作业?
答案 0 :(得分:32)
我只是尝试过,而且根据我的直觉,它最终选择了新命令(在Windows XP上)
我创建了一个包含
的批处理文件echo Hello
pause
echo world
我运行了该文件,在暂停时,添加了
echo Salute
保存并按Enter键以暂停,所有三个提示都回显到控制台。
所以,去吧!
答案 1 :(得分:21)
命令解释程序会记住批处理文件中的行位置。只要您在当前执行行位置之后修改批处理文件就可以了。
如果在此之前修改它,它将开始做奇怪的事情(重复命令等)。
答案 2 :(得分:8)
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
答案 3 :(得分:5)
简答:是的,批处理文件可以在运行时自行修改。正如其他人已经证实的那样。
多年前,在Windows 3之前,我工作的地方在MS-DOS中有一个内部菜单系统。它运行的方式非常优雅:它实际上是从批处理文件中运行的,主程序(用C编写)经过修改以运行脚本。这个技巧意味着菜单程序本身在选择运行时没有占用内存空间。这包括LAN Mail程序和3270终端程序之类的东西。
但是从自修改批处理文件运行意味着它的脚本也可以执行诸如加载TSR程序之类的事情,事实上它可以做任何你可以放在批处理文件中的东西。这使它非常强大。只有GOTO
命令不起作用,直到作者最终弄清楚如何使批处理文件为每个命令重新启动。
答案 4 :(得分:4)
几乎像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 /a n=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
答案 5 :(得分:3)
命令解释器似乎记得它正在读取的每个命令文件中的字节偏移量,但是文件本身没有被锁定,因此可以在文本编辑器运行时进行更改。
如果在记住的位置之后对文件进行了更改,则解释器应该很乐意继续执行现在修改的脚本。但是,如果在该点之前进行了更改,并且该修改会更改该点处文本的长度(例如,您已插入或删除了某些文本),则该记住的位置现在不再引用该下一个命令的开头。当解释器尝试读取下一个“行”时,它将取代不同的行,或者可能是行的一部分,具体取决于插入或删除了多少文本。如果你很幸运,它可能无法处理它所发生的任何单词,发出错误并继续执行下一行 - 但仍可能不是你想要的。
但是,通过了解正在发生的事情,您可以构建脚本以降低风险。我有脚本实现简单的菜单系统,通过显示菜单,使用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)