Я использую долго работающий командный файл. Теперь я понимаю, что мне нужно добавить еще несколько команд в конец командного файла (без изменений существующего содержимого, только несколько дополнительных команд). Возможно ли это сделать, учитывая, что большинство командных файлов читаются постепенно и выполняются один за другим? Или система считывает все содержимое файла, а затем выполняет задание?
Изменение командного файла при его запуске
Ответы (6)
Я просто попробовал, и вопреки моей интуиции он уловил новые команды в конце (в Windows XP)
Я создал командный файл, содержащий
echo Hello
pause
echo world
Я запустил файл, и пока он был приостановлен, добавил
echo Salute
Сохранил его и нажал Enter, чтобы продолжить паузу, все три запроса были выведены на консоль.
Итак, дерзайте!
Интерпретатор команд запоминает позицию строки байтовое смещение в пакетном файле. Все будет в порядке, если вы измените командный файл после текущей позиции исполняемой строки байтового смещения в конце последней проанализированной строки кода.
Если вы измените его раньше, он начнет делать странные вещи (повторять команды и т. Д.).
Пример 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 :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
Краткий ответ: да, командные файлы могут изменять себя во время работы. Как уже подтвердили другие.
Много лет назад, еще до Windows 3, в моем офисе была собственная система меню в MS-DOS. Он работал довольно элегантно: фактически он запускался из командного файла, который основная программа (написанная на C) изменяла для запуска сценариев. Этот трюк означал, что сама программа меню не занимала место в памяти, пока выполнялся выбор. И это включало такие вещи, как программа LAN Mail и программа терминала 3270.
Но запуск из самомодифицирующегося командного файла означал, что его сценарии также могли выполнять такие действия, как загрузка программ TSR, и фактически могли делать практически все, что вы могли бы поместить в командный файл. Что сделало его очень мощным. Только команда GOTO не работала, пока автор в конце концов не понял, как перезапустить сам командный файл для каждой команды.
Почти как сказал повод, 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
REM будут добавлены символы, а код под текущим расположением файла будет изменен. Работает только тогда, когда вы не меняете размер файла. Например, можно изменить set myVar=Test на REM myVar=Test
- person jeb; 07.10.2020
Интерпретатор команд, кажется, запоминает байтовое смещение в каждом командном файле, который он читает, но сам файл не заблокирован, поэтому можно вносить изменения, скажем, с помощью текстового редактора, пока он работает.
Если после этого запомненного местоположения в файл вносятся изменения, интерпретатор должен продолжить выполнение измененного сценария. Однако, если изменение было внесено до этой точки, и эта модификация изменяет длину текста в этой точке (например, вы вставили или удалили какой-то текст), это запомненное местоположение теперь больше не относится к началу этой следующей команды. . Когда интерпретатор пытается прочитать следующую «строку», он вместо этого выбирает другую строку или, возможно, часть строки, в зависимости от того, сколько текста было вставлено или удалено. Если вам повезет, он, вероятно, не сможет обработать любое слово, на которое он попадет, выдаст ошибку и продолжит выполнение со следующей строки - но все же, вероятно, не то, что вы хотите.
Однако, понимая, что происходит, вы можете структурировать свои скрипты, чтобы снизить риск. У меня есть сценарии, которые реализуют простую систему меню, отображая меню, принимая ввод от пользователя с помощью команды 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)