windows 运行时更改批处理文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/906586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 12:32:37  来源:igfitidea点击:

Changing a batch file when its running

windowsbatch-file

提问by Ritesh M Nayak

I am running a long running batch file. I now realize that I have to add some more commands at the end of the batch file (no changes to exisiting content, just some extra commands). Is it possible to do this, given that most batch files are read incrementally and executed one by one? Or does the system read the entire contents of the file and then runs the job?

我正在运行一个长时间运行的批处理文件。我现在意识到我必须在批处理文件的末尾添加更多命令(对现有内容没有更改,只是一些额外的命令)。鉴于大多数批处理文件都是增量读取并一个一个执行的,是否可以这样做?还是系统读取文件的全部内容然后运行作业?

采纳答案by Binary Worrier

I just tried it, and against my intuition, it picked up the new commands at the end (on Windows XP)

我刚刚尝试过,并且违背我的直觉,它最后选择了新命令(在 Windows XP 上)

I created a batch file containing

我创建了一个批处理文件,其中包含

echo Hello
pause
echo world

I ran the file, and while it was paused, added

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

echo Salute

Saved it and pressed enter to contine the pause, all three prompts were echoed to the console.

保存并按回车键继续暂停,所有三个提示都回响到控制台。

So, go for it!

所以,加油吧!

回答by rein

The command interpreter remembers the line position it's at in the batch file. As long as you modify the batch file after the current executing line position you'll be fine.

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

If you modify it before then it will start doing strange things (repeating commands etc..).

如果您在此之前修改它,它将开始做奇怪的事情(重复命令等)。

回答by dbenham

jeb's example is a lot of fun, but it is very dependent on the length of the text that is added or deleted. I think the counter-intuitive results are what rein meant when he said "If you modify it before then it will start doing strange things (repeating commands etc..)".

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

I've modified jeb's code to show how dynamic code of varying length can be freely modified at the beginning of an executing batch file as long as appropriate padding is in place. The entire dynamic section is completely replaced with each iteration. Each dynamic line is prefixed with a non interfering ;. This conveniently allows FOR /Fto strip the dynamic code because of the implicit EOL=;option.

我已经修改了 jeb 的代码,以展示如何在执行批处理文件的开头自由修改不同长度的动态代码,只要适当的填充就位。每次迭代都完全替换整个动态部分。每个动态行都以非干扰;. FOR /F由于隐式EOL=;选项,这方便地允许剥离动态代码。

Instead of looking for a particular line number, I look for a specific comment to locate where the dynamic code begins. This is easier to maintain.

我不是寻找特定的行号,而是寻找特定的注释来定位动态代码的开始位置。这更容易维护。

I use lines of equal signs to harmlessly pad the code to allow for expansion and contraction. Any combination of the following characters could be used: comma, semicolon, equal, space, tab and/or newline. (Of course the padding cannot begin with a semicolon.) The equal signs within the parentheses allow for code expansion. The equal signs after the parentheses allow for code contraction.

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

Note that FOR /Fstrips empty lines. This limitation could be overcome by using FINDSTR to prefix each line with the line number and then strip out the prefix within the loop. But the extra code slows things down, so it's not worth doing unless the code is dependent on blank lines.

请注意,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

The above works, but things are much easier if the dynamic code is moved to a subroutine at the end of the file. The code can expand and contract without limitation, and without the need for padding. FINDSTR is much faster than FOR /F at removing the dynamic portion. Dynamic lines can be safely be prefixed with a semicolon (including labels!). Then the FINDSTR /V option is used to exclude lines that begin with a semicolon and the new dynamic code can simply be appended.

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

@echo off
setlocal DisableDelayedExpansion
echo The starting filesize is %~z0

:loop
echo ----------------------
call :changeBatch
call :dynamicCode1
call :dynamicCode2
echo The current value=%value%
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

回答by staticsan

Short answer: yes, batch files can modify themselves whilst running. As others have already confirmed.

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

Years and years ago, back before Windows 3, the place I worked had an inhouse menu system in MS-DOS. The way it ran things was quite elegant: it actually ran from a batch file that the main program (written in C) modified in order to run scripts. This trick meant that the menu program itself was not taking up memory space whilst selections were running. And this included things like the LAN Mail program and the 3270 terminal program.

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

But running from a self-modifying batch file meant its scripts could also do things like load TSR programs and in fact could do pretty much anything you could put in a batch file. Which made it very powerful. Only the GOTOcommand didn't work, until the author eventually figured out how to make the batch file restart itself for each command.

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

回答by jeb

Nearly like reinsaid, cmd.exe remember the file position (not only the line position) it's currently is, and also for each call it push the file position on an invisble stack.

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

That means, you can edit your file while it's running behind and before the actual file position, you only need to know what you do ...

这意味着,您可以在文件在实际文件位置之前和之后运行时对其进行编辑,您只需要知道您在做什么...

A small sample of an self modifying batch
It changes the line set value=1000continuously

自改批次小样不断
换线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 

回答by jeb

The command interpreter appears to remember the byte offset within each command file it is reading, but the file itself is not locked, so it is possible to make changes, say with a text editor, whilst it is running.

命令解释器似乎会记住它正在读取的每个命​​令文件中的字节偏移量,但文件本身并未锁定,因此可以在运行时进行更改,例如使用文本编辑器。

If a change is made to the file after this remembered location, the interpreter should happily continue to execute the now modified script. However if the change is made before that point, and that modification changes the length of the text at that point (for example you've inserted or removed some text), that remembered location is now no longer referring to the start of that next command. When the interpreter tries to read the next 'line' it will instead pick up a different line, or possibly part of a line depending on how much text was inserted or removed. If you're lucky, it will probably not be able to process whatever word it happen to land on, give an error and continue to execute from the next line - but still probably not what you want.

如果在这个记住的位置之后对文件进行了更改,解释器应该很乐意继续执行现在修改过的脚本。但是,如果在该点之前进行了更改,并且该修改更改了该点文本的长度(例如,您插入或删除了一些文本),则该记住的位置现在不再指下一个命令的开始. 当解释器尝试读取下一个“行”时,它会选择不同的行,或者可能是一行的一部分,具体取决于插入或删除了多少文本。如果你很幸运,它可能无法处理它遇到的任何单词,给出错误并从下一行继续执行 - 但仍然可能不是你想要的。

However, with understanding of what's going on, you can structure your scripts to reduce the risk. I have scripts that implement a simply menu system, by displaying a menu, accepting input from the user using the choicecommand and then processing the selection. The trick is to ensure that the point where the script waits for input is near the top of the file, so that any edits you might wish to make will occur after that point and so have no nasty impacts.

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

Example:

例子:

: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)