2009-05-25 3 views
30

장기 실행 파일을 실행하고 있습니다. 이제 배치 파일의 끝 부분에 몇 가지 명령을 추가해야한다는 것을 알았습니다 (기존의 내용은 변경하지 않고 일부 추가 명령 만 변경). 대부분의 배치 파일을 점진적으로 읽고 하나씩 실행한다는 점을 고려하면이 작업을 수행 할 수 있습니까? 또는 시스템이 파일의 전체 내용을 읽은 다음 작업을 실행합니까? ,실행중인 배치 파일 변경

+3

당신 꼭 사랑 SO 빠른 응답. 이미 배치를 실행하기 시작했습니다> 질문 게시> 답변 있음> 실행 완료 전에 파일을 편집했습니다! –

+0

또한 배치 파일을 제거하거나 이름을 변경하면 현재 명령어의 오류가 완료된 순간 "배치 파일을 찾을 수 없습니다."라는 점에 유의하십시오. –

답변

28

난 그냥 그것을 시도, 나의 직관에 대해, 그것은

가 나는 파일을 실행

echo Hello 
pause 
echo world 

포함하는 배치 파일을 생성 (Windows XP에서) 마지막에 새 명령을 집어 이 일시 중지 된 동안, 세 가지 메시지는 콘솔에 반향되었다

echo Salute 

그것을 저장을 누르면 일시 정지를 contine에 입력했다.

그래서 가십시오!

15

명령 인터프리터는 배치 파일의 줄 위치를 기억합니다. 현재 실행중인 행 위치 다음에 배치 파일을 수정하는만큼 괜찮을 것입니다.

그 전에 수정하면 이상한 일을 시작합니다 (반복 명령 등).

+1

어디서든 문서화 되었습니까, 아니면 자신의 경험에서 나온 것입니까? – Benoit

+2

내 경험에 사실입니다. – UnhandledExcepSean

+0

실제로, 파서 포인터가 파일의 동일한 인덱스에 머물러 있기 때문에 인덱스 앞에 텍스트를 추가/제거하면 명령 포인터 아래에있는 내용이 바뀝니다. 이상한 일들이 일어납니다. – cat

3

거의 비슷하게 rein처럼, cmd.exe는 현재 파일 위치 (줄 위치뿐 아니라)를 기억하고 각 호출마다 파일 위치를 invisible 스택에 저장합니다. 당신이 그것을 뒤에 실행의 실제 파일 위치하기 전에, 당신은 단지 당신이 무엇을 필요로하는 동안 파일을 편집 할 수 있습니다 의미

... 자기 수정 배치의

작은 샘플
그것은 라인을 변경 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 
+0

+1 그게 가능한 일의 재미있는 예입니다. – dbenham

7

젭의 예는 많은 재미이지만, 추가 또는 삭제되는 텍스트의 길이에 매우 의존한다. 나는 반 직관적 인 결과가 "당신이 그 전에 그것을 수정하면 이상한 일들을 시작하게 될 것입니다 (반복 명령 등 ..)"라고 말했을 때 고의가 의미하는 바라고 생각합니다.

적절한 패딩이있는 한 실행중인 배치 파일의 시작 부분에서 다양한 길이의 동적 코드를 자유롭게 수정하는 방법을 보여주기 위해 jeb의 코드를 수정했습니다. 전체 동적 섹션이 각 반복과 완전히 교체됩니다. 각 동적 라인 앞에는 간섭이없는 ;이라는 접두어가 붙습니다. 이렇게하면 암시적인 EOL=; 옵션 때문에 FOR /F에서 동적 코드를 제거하는 것이 편리합니다.

특정 줄 번호를 찾는 대신 동적 코드가 시작되는 위치를 찾기 위해 특정 주석을 찾습니다. 이것은 유지하기가 더 쉽습니다.

나는 등호선을 사용하여 코드를 무해하게 확장하고 수축을 허용합니다. 다음 문자 조합을 사용할 수 있습니다 : 쉼표, 세미콜론, 등호, 공백, 탭 및/또는 개행. (물론 패딩은 세미콜론으로 시작할 수 없습니다.) 괄호 안의 등호는 코드 확장을 허용합니다. 괄호 뒤에있는 등호는 코드 축소를 허용합니다.

FOR /F은 빈 줄을 제거합니다.이 제한은 FINDSTR을 사용하여 각 행 앞에 줄 x 호를 붙인 다음 루프 내의 접 두부를 제거하여 극복 할 수 있습니다. 그러나 여분의 코드는 작업 속도를 늦추므로 코드가 빈 줄에 의존하지 않는 한 할 가치가 없습니다. 동적 코드가 파일의 끝에 서브 루틴으로 이동하는 경우

@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 
+1

EDIT - 마지막 코드를 수정하여 짝수 레이블이 접두사가 될 수 있음을 보여주기 때문에 동적 코드에 쉽게 포함될 수 있습니다. – dbenham

2

짧은 대답 : 예, 배치 파일은 실행 중에 스스로 수정할 수 있습니다. 다른 사람들이 이미 확인한 것처럼.

이전 버전의 Windows 3 이전 버전에서는 MS-DOS에서 일한 메뉴 시스템을 사용했습니다. 스크립트가 실행되는 방식은 매우 우아합니다. 실제로 스크립트를 실행하기 위해 수정 된 기본 프로그램 (C로 작성된)이 배치 파일에서 실행되었습니다. 이 트릭은 선택 사항이 실행되는 동안 메뉴 프로그램 자체가 메모리 공간을 차지하지 않는다는 것을 의미했습니다. 여기에는 LAN 메일 프로그램 및 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)