Изменение командного файла при его запуске

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

Ответ 1

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

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

echo Hello
pause
echo world

Я запустил файл, и пока он был приостановлен, добавлен

echo Salute

Сохраняя его и нажав enter, чтобы остановить паузу, все три подсказки были эхом отданы на консоль.

Итак, пойдите для этого!

Ответ 2

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

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

Ответ 3

jeb example - это очень весело, но он очень зависит от длины добавляемого или удаляемого текста. Я думаю, что контр-интуитивные результаты - это то, что означало подпорка, когда он сказал: "Если вы измените его до этого, он начнет делать странные вещи (повторять команды и т.д.)".

Я изменил код 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

Ответ 4

Короткий ответ: да, пакетные файлы могут изменять себя во время работы. Как уже подтвердили другие.

Годы и годы назад, перед Windows 3, место, в котором я работал, имело внутреннюю систему меню в MS-DOS. То, как он управлял вещами, был довольно изящным: он фактически запускался из командного файла, который основная программа (написанная на языке C) модифицирована для запуска скриптов. Этот трюк означал, что сама программа меню не занимала память, пока выполнялись выборы. И это включало такие вещи, как программа LAN Mail и терминальная программа 3270.

Но запуск из самомодулируемого командного файла означал, что его скрипты также могут делать такие вещи, как загрузка программ TSR, и на самом деле могли бы сделать практически все, что вы могли бы добавить в пакетный файл. Это сделало его очень мощным. Только команда GOTO не работала, пока автор в конечном итоге не выяснил, как сделать командный файл перезагрузкой для каждой команды.

Ответ 5

Примерно так же, как сказал rein, cmd.exe запоминает позицию файла (не только позицию строки), которой он является в настоящее время, а также при каждом вызове выдвигает позицию файла в невидимом стеке.

Это означает, что вы можете редактировать свой файл, пока он работает позади и до фактической позиции файла, вам нужно только знать, что вы делаете...

Небольшой образец самоизменяющейся партии
Изменяет set value=1000 линии 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 

Ответ 6

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

Если после этого запомненного местоположения происходит изменение файла, интерпретатор должен с радостью продолжить выполнение теперь измененного script. Однако, если изменение сделано до этой точки, и эта модификация изменяет длину текста в этой точке (например, вы вставили или удалили какой-либо текст), это запоминающееся местоположение теперь больше не относится к началу следующей команды, Когда интерпретатор пытается прочитать следующую "строку", он вместо этого возьмет другую строку или, возможно, часть строки в зависимости от того, сколько текста было вставлено или удалено. Если вам повезет, он, вероятно, не сможет обработать любое слово, с которым он столкнулся, дать ошибку и продолжить выполнение со следующей строки - но все же, вероятно, не то, что вы хотите.

Однако, понимая, что происходит, вы можете структурировать свои скрипты, чтобы снизить риск. У меня есть сценарии, которые реализуют систему простого меню, отображая меню, принимая ввод от пользователя с помощью команды choice, а затем обрабатывая выделение. Хитрость заключается в том, чтобы точка, в которой ожидаемый вход script ожидает ввода, находится ближе к верхней части файла, так что после этого изменения будут сделаны любые изменения, которые могут возникнуть после этого, и поэтому не имеют неприятных ударов.

Пример:

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