2009-05-25 41 views
30

Estoy ejecutando un archivo por lotes de ejecución larga. Ahora me doy cuenta de que tengo que agregar algunos más comandos al final del archivo por lotes (no hay cambios en el contenido existente, solo algunos comandos adicionales). ¿Es posible hacer esto, dado que la mayoría de los archivos por lotes se leen de forma incremental y se ejecutan uno por uno? ¿O el sistema lee todo el contenido del archivo y luego ejecuta el trabajo?Cambiar un archivo por lotes cuando se está ejecutando

+3

Usted gotta love respuestas tan rápido. Ya comenzó a ejecutar el lote> publicó una pregunta> obtuvo una respuesta> editó su archivo antes de la ejecución completada. –

+0

También tenga en cuenta que cuando se elimine o se cambie el nombre del archivo por lotes, en el momento en que finalice la instrucción actual se generará un error: "No se puede encontrar el archivo por lotes". –

Respuesta

28

yo sólo lo han probado, y en contra de mi intuición, se recogieron los nuevos comandos al final (en Windows XP)

he creado un archivo por lotes que contiene

echo Hello 
pause 
echo world 

me encontré con el archivo, y al mismo tiempo en que se interrumpió, agregó

echo Salute 

guardado y pulsa eNTER para contine la pausa, los tres indicaciones se hicieron eco de la consola.

Así que, ¡adelante!

15

El intérprete de comandos recuerda la posición de línea en el archivo por lotes. Siempre que modifiques el archivo por lotes después de la posición actual de la línea de ejecución, estarás bien.

Si lo modifica antes, entonces comenzará a hacer cosas extrañas (repetir comandos, etc.).

+1

¿Está documentado en alguna parte, por favor o es por su propia experiencia? – Benoit

+2

Esto es cierto en mi experiencia. – UnhandledExcepSean

+0

En realidad, lo que hará es que el puntero del analizador permanecerá en el mismo índice en el archivo, por lo que agregar/eliminar texto antes del índice cambiará lo que está debajo del puntero de instrucción. Cosas raras suceden, de hecho. – cat

3

Casi como rein dijo, cmd.exe recuerda la posición del archivo (no solo la posición de línea) que es actualmente, y también para cada llamada empuja la posición del archivo en una pila invisible.

Eso significa, puede editar su archivo mientras se está ejecutando detrás y delante de la posición real del archivo, sólo necesita lo que haces ...

Una pequeña muestra de un lote de auto modificación
Cambia la línea set value=1000 ejemplo continuamente

@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 Ese es un ejemplo divertido de lo que es posible :) – dbenham

7

de Jeb es un montón de diversión, pero es muy dependiente de la longitud del texto que se añade o se elimina. Creo que los resultados contrarios a la intuición son lo que quiso decir cuando dijo "Si lo modificas antes, entonces comenzará a hacer cosas extrañas (repetir comandos, etc.)".

Modifiqué el código de jeb para mostrar cómo se puede modificar libremente el código dinámico de longitud variable al comienzo de un archivo por lotes en ejecución, siempre que el relleno apropiado esté en su lugar. Toda la sección dinámica se reemplaza por completo con cada iteración. Cada línea dinámica tiene el prefijo ; que no interfiere. Esto permite convenientemente FOR /F quitar el código dinámico debido a la opción implícita EOL=;.

En lugar de buscar un número de línea particular, busco un comentario específico para ubicar dónde comienza el código dinámico. Esto es más fácil de mantener.

Utilizo líneas de signos iguales para rellenar el código de forma inofensiva y permitir la expansión y la contracción. Se puede usar cualquier combinación de los siguientes caracteres: coma, punto y coma, igual, espacio, pestaña y/o nueva línea. (Por supuesto, el relleno no puede comenzar con un punto y coma.) Los signos de igual dentro de los paréntesis permiten la expansión del código. Los signos de igual después del paréntesis permiten la contracción del código.

Tenga en cuenta que FOR /F elimina las líneas vacías.Esta limitación se puede superar mediante el uso de FINDSTR para prefijar cada línea con el número de línea y luego quitar el prefijo dentro del ciclo. Pero el código adicional ralentiza las cosas, por lo que no vale la pena hacerlo a menos que el código dependa de líneas en blanco.

@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 

Los trabajos anteriores, pero las cosas son mucho más fáciles si el código dinámico se mueve a una subrutina al final del archivo. El código puede expandirse y contraerse sin limitación, y sin la necesidad de relleno. FINDSTR es mucho más rápido que FOR/F para eliminar la parte dinámica. Las líneas dinámicas pueden ser prefijadas con un punto y coma (incluidas las etiquetas). Luego, la opción FINDSTR/V se usa para excluir líneas que comienzan con un punto y coma y el nuevo código dinámico puede simplemente anexarse.

@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

EDITAR - Modifiqué el último código para demostrar que incluso las etiquetas pueden tener prefijos, por lo que pueden incluirse fácilmente en el código dinámico. – dbenham

2

Respuesta breve: sí, los archivos por lotes se pueden modificar mientras se están ejecutando. Como otros ya han confirmado.

Hace años y años, antes de Windows 3, el lugar donde trabajaba tenía un sistema de menú interno en MS-DOS. La forma en que ejecutó las cosas fue bastante elegante: en realidad se ejecutó desde un archivo por lotes que el programa principal (escrito en C) modificó para ejecutar scripts. Este truco significaba que el programa de menú no ocupaba espacio en la memoria mientras se ejecutaban las selecciones. Y esto incluía cosas como el programa LAN Mail y el programa de terminal 3270.

Pero al ejecutar desde un archivo por lotes auto-modificable, sus scripts también podían hacer cosas como cargar programas TSR y de hecho podían hacer casi cualquier cosa que pudieras poner en un archivo por lotes. Lo que lo hizo muy poderoso. Solo el comando GOTO no funcionó, hasta que el autor eventualmente descubrió cómo hacer que el archivo por lotes se reiniciara para cada comando.

2

El intérprete de comandos parece recordar el desplazamiento de bytes dentro de cada archivo de comando que está leyendo, pero el archivo no está bloqueado, por lo que es posible realizar cambios, por ejemplo con un editor de texto, mientras se está ejecutando.

Si se realiza un cambio en el archivo después de esta ubicación recorrida, el intérprete debería continuar ejecutando el script modificado. Sin embargo, si el cambio se realiza antes de ese punto, y esa modificación cambia la longitud del texto en ese punto (por ejemplo, ha insertado o eliminado algún texto), esa ubicación recordada ya no se refiere al inicio de ese siguiente comando . Cuando el intérprete intenta leer la siguiente "línea", en lugar de eso recoge una línea diferente, o posiblemente parte de una línea, según la cantidad de texto insertado o eliminado. Si tiene suerte, probablemente no podrá procesar la palabra en la que aterrice, generará un error y continuará ejecutándose desde la siguiente línea, pero probablemente no sea lo que usted desea.

Sin embargo, con la comprensión de lo que está sucediendo, puede estructurar sus secuencias de comandos para reducir el riesgo. Tengo scripts que implementan un sistema de menú simple, mostrando un menú, aceptando la entrada del usuario usando el comando choice y luego procesando la selección. El truco consiste en asegurarse de que el punto donde el script espera la entrada está cerca de la parte superior del archivo, de modo que cualquier edición que desee realizar se producirá después de ese punto y, por lo tanto, no tendrá ningún impacto desagradable.

Ejemplo:

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