2009-05-25 50 views
30

我正在運行長時間運行的批處理文件。我現在意識到,我必須在批處理文件的末尾添加更多的命令(不更改現有內容,只是一些額外的命令)。是否有可能這樣做,因爲大多數批處理文件是逐步讀取並逐個執行的?或者系統讀取文件的全部內容,然後運行作業?在運行時更改批處理文件

+3

你得愛這麼快的反應。你已經開始運行批處理>發佈了一個問題>有一個答案>在執行完成之前編輯你的文件! – 2014-04-09 04:21:23

+0

另請注意,當批處理文件被刪除或重命名時,當前指令完成時會拋出一個錯誤:「無法找到批處理文件。」 – 2014-07-28 23:34:47

回答

28

我剛剛試了一下,對我的直覺,它在最後拿起新的命令(在Windows XP)

我創建了一個包含

echo Hello 
pause 
echo world 

我跑了文件的批處理文件,並在暫停時添加

echo Salute 

保存並按下Enter鍵以控制暫停,所有三個提示都被回顯到控制檯。

所以,去吧!

15

命令解釋程序會記住它在批處理文件中的行位置。只要你在當前正在執行的行位置後修改批處理文件,你就會好起來的。

如果你修改它之前,它會開始做奇怪的事情(重複命令等..)。

+1

請記錄在任何地方,或者這是來自您自己的經驗? – Benoit 2012-03-09 13:07:40

+2

這是我的經驗。 – UnhandledExcepSean 2012-03-09 13:17:46

3

幾乎像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 
7

傑布的例子是很多的樂趣,但它是非常依賴於被添加或刪除的文本的長度。我認爲反直覺的結果是當他說「如果你修改它之前它會開始做奇怪的事情(重複命令等)」意味着什麼。

我修改了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 
2

簡答:是的,批處理文件可以在運行時自行修改。正如其他人已經證實。

幾年前,在Windows 3之前,我工作的地方在MS-DOS中有一個內部菜單系統。它運行的方式非常優雅:它實際上是從主程序(用C編寫)修改以運行腳本的批處理文件運行的。這個竅門意味着菜單程序本身並沒有佔用內存空間,而選擇正在運行。這包括諸如LAN Mail程序和3270終端程序之類的東西。

但是從一個自我修改的批處理文件運行意味着它的腳本也可以執行諸如加載TSR程序之類的事情,事實上可以做任何你可以放在批處理文件中的東西。這使得它非常強大。只有GOTO命令不起作用,直到作者最終想出瞭如何爲每個命令重新啓動批處理文件。

2

命令解釋程序似乎記住了它正在讀取的每個命​​令文件內的字節偏移量,但是文件本身並未鎖定,所以可以在文本編輯器運行時進行更改。

如果在記住該位置後對文件進行更改,解釋器應該繼續執行現在修改的腳本。但是,如果在該點之前進行了更改,並且該修改會更改此時文本的長度(例如,您插入或刪除了一些文本),則記住的位置現在不再指該下一個命令的開始。當解釋器試圖讀取下一個'行'時,它將取而代之選擇不同的行,或者可能是行的一部分,取決於插入或刪除多少文本。如果你幸運的話,它可能無法處理它發生的任何單詞登陸,發出錯誤並繼續從下一行執行 - 但仍可能不是你想要的。

但是,通過了解正在發生的事情,您可以構建腳本以降低風險。我的腳本實現了簡單的菜單系統,通過顯示菜單,使用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)