2009-05-25 5 views
30

Я запускаю пакетный файл с длительным сроком действия. Теперь я понимаю, что мне нужно добавить еще несколько команд в конце командного файла (без изменений в существующем контенте, а также в некоторых дополнительных командах). Можно ли это сделать, учитывая, что большинство командных файлов считываются инкрементально и выполняются один за другим? Или система считывает все содержимое файла, а затем запускает задание?Изменение командного файла при его запуске

+3

Вы должны любить так быстро ответы. Вы уже запустили пакет> опубликованный вопрос> получили ответ> отредактировали файл до завершения выполнения! –

+0

Также обратите внимание, что когда пакетный файл удаляется или переименовывается, момент, когда текущая команда завершила ошибку, будет выдана: «Пакетный файл не может быть найден». –

ответ

28

Я просто попытался его, и против своей интуиции, она подобрала новые команды в конце (на Windows XP)

Я создал пакетный файл, содержащий

echo Hello 
pause 
echo world 

Я побежал файл, и в то время как она была приостановлена, добавил

echo Salute 

Сохраненный его и прижала ввести в Contine паузу, все три подсказки были повторены на консоль.

Итак, идите!

15

Командный интерпретатор запоминает позицию линии, в которой он находится в пакетном файле. Пока вы изменяете командный файл после текущей позиции линии, вы будете в порядке.

Если вы измените его до этого, он начнет делать странные вещи (повторяющиеся команды и т. Д.).

+1

Является ли это документированным где угодно, или это из вашего собственного опыта? – Benoit

+2

Это верно в моем опыте. – UnhandledExcepSean

+0

На самом деле, что он будет делать, указатель синтаксического анализатора останется в том же индексе в файле, поэтому добавление/удаление текста перед индексом сдвинет то, что находится под указателем инструкции. Действительно, странные вещи случаются. – cat

3

Почти как rein said, 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 
+0

+1 Это забавный пример того, что возможно :) – dbenham

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

EDIT - я изменил последний код, чтобы продемонстрировать, что даже метки могут быть префиксными, поэтому их можно легко включить в динамический код. – dbenham

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) 
Смежные вопросы